GitNexus shipped a CatalogHome grid, DropZone local analyze, onboarding phases, and server-connect wizards. Beskid Nexus is a hosted public explorer for pre-indexed repos — visitors arrive to browse graphs, not to analyze local folders or configure LLM providers.
Contracts and edge cases
Platform spec article
Contracts and edge cases
Spec standingStandard
-
Nexus opens the first indexed repo graph on / instead of a catalog grid or onboarding flows.
Context
Decision
/must load the graph explorer, selecting the first enabled catalog entry withindexed: trueordered bysortOrder.- Deep link
?repo=<catalog-id>selects an explicit entry when present. - CatalogHome, DropZone, QueryFAB, Graph RAG chat, and hosted OnboardingGuide are removed from public UI.
- Repo switching happens via navbar
<Select>, not a separate home phase. - When no indexed repo exists, show empty state + sign-in CTA for repo owners — not a marketing catalog grid.
Admin actions (add repo, re-index) must be overlays (dialog/sheet), not full-page route phases.
Consequences
gitnexus-webApp phases reduce toboot | setup | server-down | explorer.- E2E tests target graph canvas visibility and repo selector — not onboarding wizards.
- Cached LadybugDB exports remain the only graph source for public visitors.
Status
Accepted — supersedes GitNexus CatalogHome as the default Nexus entry experience.
-
Per-repo CRUD and analyze triggers require GitHub-verified ownership via hub token — not env admin rosters.
Context
GitNexus used a static admin roster for catalog mutations. Beskid Nexus indexes public graphs for many repositories — administration should follow GitHub ownership, not a deployment-time username list that does not scale across contributors and org repos.
Instance operators still need bootstrap authority for auth hub pairing and secrets — distinct from day-to-day repo administration.
Decision
POST/PATCH/DELETE /api/admin/catalog*andPOST .../analyzeandPOST .../refresh-docsrequire the session user to be owner or admin of the entry'sgitUrlper GitHub API (GET /repos/{owner}/{repo}with hub user token).GET /api/auth/memust exposeownedRepoIds— catalog ids the user may administer — for UI gating.requireAdmin(env roster) is retained only forPOST /api/admin/auth/pairand first-run setup endpoints.- Ownership verification may be cached up to 15 minutes per
(login, gitUrl). - Non-owners receive
403on mutating routes.
Consequences
github-ownership.tscentralizes URL parsing and API checks.- Frontend admin sheet gates on
ownedRepoIds.includes(activeEntry.id). - Public catalog and graph routes remain unauthenticated.
Status
Accepted — reference implementation in
beskid_nexus/gitnexus/src/server/nexus/github-ownership.ts. -
AI-generated codeDoc describes repository code only; platform spec body text never inlined — specLinks are validated hrefs from a read-only index.
Context
Nexus runs a hidden doc-maintenance worker after analyze. Without a hard boundary, generated node text could duplicate or paraphrase normative platform-spec MDX — creating two sources of truth and stale copies when spec changes.
Platform spec on
site/websiteleads code per project policy. Nexus is a reader of spec URLs, not a publisher of spec prose.Decision
codeDocis repo-scoped AI documentation — generated from graph metadata and short file snippets only.- Platform spec MDX bodies are never fed into code-doc prompts as source material.
- A read-only spec link index (
spec-index.json) stores titles, slugs, and headings for link discovery only. resolve_spec_linkstool returns candidate{ title, href }from the index; unknown hrefs must be rejected at commit.specLinksare 0–3 validated pointers — not summaries of spec content.code-doc-validatoranti-copy guard rejects records containing ≥40 consecutive characters matching spec index excerpts.- Graph API and UI must expose
codeDocandspecLinksas separate fields/sections.
When a topic is fully covered by platform spec,
codeDocstays brief and defers viaspecLinks.Consequences
- Two-phase doc pipeline: Phase A writes
codeDocfrom graph context; Phase B attaches links via index search. - Public API and UI must not mention OpenRouter, prompts, or AI vendors.
- Spec deploys require Nexus restart or index rebuild to pick up new link targets.
Status
Accepted — normative contracts in design model and contracts.
-
Signed-in users connect MCP clients to same-origin /api/mcp using deployment Bearer token; UI documents endpoint without exposing secrets.
Context
Nexus exposes graph query tools over MCP Streamable HTTP for IDE and agent integrations. Operators deploy a shared
NEXUS_MCP_AUTH_TOKENsecret. Users need a discoverable, copy-friendly surface — without embedding secrets in client bundles or public pages.Decision
- MCP must be mounted at
/api/mcpon the Nexus deployment origin. - All MCP requests must require
Authorization: Bearer $NEXUS_MCP_AUTH_TOKEN. - Header button Connect MCP must appear for signed-in users (any authenticated session).
- ConnectMcpDialog shows:
- Endpoint URL (
{window.location.origin}/api/mcp) - Auth header template with placeholder token guidance
- Copy buttons for URL and header format
- Link to MCP contracts
- Endpoint URL (
- The live Bearer token must not be returned by public or session API routes — operators supply it via deployment env (Coolify, etc.).
Consequences
gitnexus-webshipsconnect-mcp-dialog.tsxwired intoNexusAppShell.- MCP shares the cached graph data plane with public
/api/graph— no parallel index. - Platform spec documents the contract; Nexus UI links here for operator and integrator reference.
Status
Accepted — complements GitNexus MCP engine retention under Beskid Nexus product shell.
- MCP must be mounted at
0 revisions (git unavailable at build; counts may be empty)
No commits recorded for this path.
| Section id | Required | Found |
|---|---|---|
what-this-feature-specifies | yes | yes |
implementation-anchors | yes | yes |
Full tree: run pnpm verify:platform-spec-layout (writes src/generated/platform-spec-layout-report.json).
Public catalog
Section titled “Public catalog”GET /api/catalog
Section titled “GET /api/catalog”Returns:
{ "entries": [ { "id": "beskid", "displayName": "Beskid", "description": "Language platform superrepo", "gitUrl": "https://github.com/Cyber-Nomad-Collective/beskid", "defaultBranch": "main", "sortOrder": 0, "indexed": true, "registryName": "beskid-main", "lastIndexedCommit": "abc123", "indexedAt": "2026-05-30T12:00:00.000Z", "stats": { "nodes": 12000, "relationships": 45000 }, "docStatus": "ready" } ]}| Rule | Policy |
|---|---|
| NX-01 | Response must not expose internal paths, OpenRouter model names, or prompt text |
| NX-02 | Entries with indexed: false may appear in catalog but must not be selected as landing default |
| NX-03 | docStatus is coarse public state only — no AI pipeline error strings |
GET /api/catalog/:id
Section titled “GET /api/catalog/:id”Returns: single PublicCatalogEntry or 404.
Public graph
Section titled “Public graph”GET /api/graph?repo=<registryName>
Section titled “GET /api/graph?repo=<registryName>”Returns:
{ "nodes": [ { "id": "n_42", "labels": ["Function"], "properties": { "name": "resolveImports", "path": "compiler/crates/beskid_analysis/src/resolve/mod.rs", "codeDoc": "Resolves import declarations against the module index for the active compilation unit.", "specLinks": [ { "title": "Dependency graph and cycle policy", "href": "/platform-spec/compiler/resolution-and-projects/dependency-graph-and-cycle-policy/" } ] } } ], "relationships": []}| Rule | Policy |
|---|---|
| NX-G-01 | repo query must match registryName of an indexed catalog entry |
| NX-G-02 | properties.codeDoc and properties.specLinks must be omitted when no CodeDocRecord exists |
| NX-G-03 | When present, codeDoc and specLinks must be attached separately — never concatenated into one string |
| NX-G-04 | Graph stream must be the cached LadybugDB export; clients must not trigger analyze from this route |
Authenticated session
Section titled “Authenticated session”GET /api/auth/me
Section titled “GET /api/auth/me”Returns:
{ "login": "octocat", "name": "Octocat", "avatarUrl": "https://avatars.githubusercontent.com/u/1?v=4", "ownedRepoIds": ["my-demo", "beskid"]}| Rule | Policy |
|---|---|
| NX-A-01 | ownedRepoIds lists catalog id values where the session user is GitHub owner or admin |
| NX-A-02 | Unauthenticated requests must return 401 |
| NX-A-03 | Ownership must be verified via GitHub API with hub user token — not a static env roster |
Repo-owner admin
Section titled “Repo-owner admin”All routes below require authenticated session and GitHub ownership of the target entry’s gitUrl.
POST /api/admin/catalog
Section titled “POST /api/admin/catalog”Body:
{ "displayName": "My Demo", "description": "Example Beskid repo", "gitUrl": "https://github.com/octocat/demo", "defaultBranch": "main"}Returns: created PublicCatalogEntry or 403 when user is not repo owner.
PATCH /api/admin/catalog/:id
Section titled “PATCH /api/admin/catalog/:id”Body: partial metadata fields (displayName, description, gitUrl, defaultBranch, sortOrder).
DELETE /api/admin/catalog/:id
Section titled “DELETE /api/admin/catalog/:id”Removes entry from global registry and associated index artifacts.
POST /api/admin/catalog/:id/analyze
Section titled “POST /api/admin/catalog/:id/analyze”Enqueues clone + analyze worker for the entry.
POST /api/admin/catalog/:id/refresh-docs
Section titled “POST /api/admin/catalog/:id/refresh-docs”Enqueues code-doc maintenance for the entry (server-side only).
| Rule | Policy |
|---|---|
| NX-ADM-01 | Non-owners must receive 403 — not 404 — to avoid catalog id enumeration ambiguity |
| NX-ADM-02 | Operator requireAdmin must remain only for setup pairing (POST /api/admin/auth/pair) and instance bootstrap |
| NX-ADM-03 | Ownership checks may be cached up to 15 minutes per (login, gitUrl) pair |
CodeDocRecord validation
Section titled “CodeDocRecord validation”| Rule | Policy |
|---|---|
| NX-DOC-01 | codeDoc must describe what the code does in the repo — generated from graph metadata and short file snippets only |
| NX-DOC-02 | codeDoc must not contain platform-spec MDX body text or long excerpts from the spec link index |
| NX-DOC-03 | Anti-copy guard must reject records where codeDoc contains ≥40 consecutive characters matching any spec index excerpt |
| NX-DOC-04 | specLinks must contain 0–3 entries; each href must exist in the built spec link index |
| NX-DOC-05 | Unknown href values must be dropped at commit time — never stored or exposed |
| NX-DOC-06 | Regeneration must skip entities whose contentHash is unchanged |
| NX-DOC-07 | Similar sibling codeDoc text (Jaccard > 0.85) should be deduplicated before persist |
Spec link index (read-only)
Section titled “Spec link index (read-only)”Built at boot from NEXUS_SPEC_ROOT (default: site/website/src/content/docs/platform-spec). Index stores slug, title, href, and heading outline for link lookup only — full MDX bodies must not be fed into code-doc generation prompts.
POST /api/mcp (Streamable HTTP)
Section titled “POST /api/mcp (Streamable HTTP)”| Rule | Policy |
|---|---|
| NX-MCP-01 | Endpoint must be mounted at /api/mcp on the Nexus origin |
| NX-MCP-02 | Requests must include Authorization: Bearer <NEXUS_MCP_AUTH_TOKEN> |
| NX-MCP-03 | Missing or invalid Bearer must return 401 |
| NX-MCP-04 | MCP tools expose graph query capabilities from the cached index — same data plane as public graph API |
| NX-MCP-05 | Connect MCP dialog must document endpoint and header format; token is operator-supplied via deployment env |
Example client configuration:
URL: https://nexus.beskid-lang.org/api/mcpHeader: Authorization: Bearer $NEXUS_MCP_AUTH_TOKENEdge cases
Section titled “Edge cases”| Scenario | Expected behavior |
|---|---|
| Catalog empty | Landing empty state; repo selector disabled |
| Entry registered but not indexed | Visible in selector with disabled graph; owner may trigger analyze |
| Analyze in progress | Graph route returns 409 or stale export per server policy; UI shows loading state |
| Doc job failed | docStatus: failed; graph still served; codeDoc absent on affected entities |
Non-GitHub gitUrl on create | 400 — ownership cannot be verified |
| GitHub API rate limit | Ownership cache reduces calls; transient failures must fail closed (403) |
| Spec index stale after site deploy | Operator restarts Nexus or triggers spec index rebuild at boot |
Type reference
Section titled “Type reference”interface PublicCatalogEntry { id: string; displayName: string; description: string; gitUrl: string; defaultBranch?: string; sortOrder: number; indexed: boolean; registryName?: string; lastIndexedCommit?: string; indexedAt?: string; stats?: { nodes: number; relationships: number }; docStatus?: 'idle' | 'running' | 'failed' | 'ready';}Normative TypeScript mirrors live in beskid_nexus/gitnexus/src/server/nexus/types.ts.