Skip to main content
CID222 Docs

Track repositories for code safety

Connect a repository host, assign repositories to tenants, and keep the code-safety auditor current with a baseline scan and an incremental scan on every push.

  • Version: 0.4
  • Feature: codesafety
  • Role: superadmin
  • Type: task

Administration → Repository Tracking points the CID222 code-safety auditor at your source code. You add a connector for a repository host, assign individual repositories to tenants, and CID222 runs a baseline scan on assignment and an incremental scan on every push. Findings are readable per repository, and you can ask a model to analyse selected files afterwards.

What do I need?

Licence
Enterprise
Role
superadmin

Prerequisites

  • You are signed in as the superadmin. An admin_user is offered this page and refused by every call it makes.
  • `REPO_TRACKING_ENABLED` is `true` in the appliance's compose environment. Off, the module registers nothing and every route on this page returns 404.
  • A cid-code-safety auditor is reachable at `CODE_SAFETY_SERVICE_URL`, with `CODE_SAFETY_SERVICE_ENABLED` set to `true` and the deep path enabled on the auditor itself.
  • `JWT_SECRET` is set and persistent. Connector credentials are encrypted with a key derived from it, and the gateway refuses to store them otherwise.
  • `PUBLIC_BASE_URL` is set if you want webhook-driven scans rather than polling.

Warning

This feature is off by default at three independent gates: REPO_TRACKING_ENABLED (default false), CODE_SAFETY_SERVICE_ENABLED (default false), and the auditor's own deep-path switch. All three must be on, on top of an Enterprise licence.

Who can use this page

The page registry offers Repository Tracking to superadmin, admin_user and viewer, but the backend admits only superadmin — a connector reads every repository assigned through it, and an assignment decides whose scope the findings land in. An admin_user who opens the page is refused by the page itself with Only the superadmin can manage repository connectors, because an assignment decides whose scope findings land in. A viewer sees the page and its live-looking buttons, and discovers on click that every write is refused with READ_ONLY_ROLE.

Add a connector

A connector is one repository host plus the credentials that read every repository assigned through it. Four types exist: GitHub / GHES, GitLab, Gitea / Forgejo and Generic git (polled). There is no Bitbucket and no Azure DevOps.

Open the dialog

Select Administration → Repository Tracking, then Add connector.

The Add repository connector dialog opens with GitHub / GHES selected.

Choose the type

Set Type.

The dialog's description and the host field change with the type. For Generic git (polled) the note reads: CID generates the deploy key itself and shows you only the public half; there is no API and no webhooks, so changes are found by polling.

Name it and set the host

Enter a Display name, then fill the host field.

For GitHub / GHES the field is GitHub Enterprise Server URL and you leave it blank for github.com — the host only, never a repository URL. For Generic git (polled) the field is Clone URL and it is the repository itself. Entering a repository URL where a host is expected raises an inline warning before you submit.

Supply credentials, or let GitHub create them

Leave the credential fields alone and optionally set GitHub organization. The button reads Continue to GitHub; selecting it stages a disabled connector and hands you to GitHub, which creates the App, asks you to approve it, and then asks which repositories it may read. GitHub sends back the App id, private key and webhook secret — nothing is copied by hand. The App requests read-only access to contents and metadata, and the push event.

The submit button reads Create, or Continue to GitHub on the manifest path.

Take the one-time secrets

Read the Connector created dialog and copy what it shows before closing it.

Depending on the type it shows a Deploy key (public half) to add to the repository as a read-only deploy key, or a Payload URL and Secret to add as a webhook. Both are shown once. Without PUBLIC_BASE_URL the dialog says so instead of inventing a URL.

Test the connection

Select Test on the connector's row.

Success toasts Connected — {n} repositories visible and the Status tag turns active. Failure shows the host's own error text and stores it against the row, where the error tag exposes it as a tooltip.

Danger

There is no edit dialog and no key-rotation screen. PATCH /admin/repo-connectors/:id exists in the API, but the dashboard never calls it. To change a host, a token or a key you delete the connector — which removes every repository tracked through it, along with their findings and stored analyses — and create it again.

Assign a repository

An assignment binds one repository, through one connector, to one tenant. It cannot be bound to a tenant group. The tenant is the scope the auditor files findings under.

Start the assignment

Select Assign repo on the connector's row.

The button is disabled when no provider is available for the connector type. The Assign a repository dialog states: a baseline scan is queued immediately, and findings land in the chosen tenant's scope on the auditor.

Pick the repository and the tenant

Choose Repository from the list the connector can see, then choose Tenant.

The repository list comes from the host. If the connector cannot read it, an amber banner shows the host's own message instead of a list.

Queue the baseline

Select Assign.

A Repository assigned — baseline queued toast appears and the row joins Tracked repositories with Baseline showing pending, then running, then complete.

While a baseline or an incremental scan is live, the list refreshes every 5 seconds and the Baseline cell carries a progress string — findings so far, files scanned against the total, and an estimate derived from this repository's own previous run. It never invents a denominator it does not have.

Keep it current

Once the baseline completes, CID222 tracks the repository's default branch only.

TriggerWhat happens
A push webhook arrivesCID222 verifies the HMAC signature over the raw body, then queues an increment
A generic-git repository is polledA cluster-wide poll runs every 30 seconds and backs off to 30 minutes for a repository that has been quiet for a day
You select RebaselineThe indexed commit is cleared and a full baseline is queued again

An increment never scans the commits the webhook named. It always diffs the last indexed commit against the current head, which is why an outage catches up by itself, why several pushes coalesce into one scan, and why Rebaseline is also the fix after a force-push. The indexed commit advances only when the auditor reports the job completed.

Two caps apply. More than 500 changed files rebaselines instead of incrementing, and an individual file over 1 MiB is skipped.

Warning

Every webhook failure answers 404 — a wrong signature, a disabled connector, a connector with no secret, an unparseable body and rate limiting are deliberately indistinguishable, so an unauthenticated caller cannot enumerate connectors. Use the History dialog on the assignment, not the HTTP status, to find out why a push produced no scan.

Read the findings

Select Findings on a tracked repository — enabled only once a scan has completed. The dialog has three tabs.

  • Findings — the auditor's findings, filterable by text and by the severities the response itself declares. The header line names the finding count, the job's decision and the job id. Select the checkboxes to choose files, then Analyze with the model.
  • Files read — what the scan read and what it excluded, with the reason for each exclusion.
  • Structure — the outline of the repository the model was shown, as files, symbols and import edges.

Danger

A scan marked degraded means unknown, not clean. The dialog says so: an analyzer was unavailable, so treat the result as unknown. A known auditor-side defect makes nearly every baseline report degraded today, so read that banner as a real caveat rather than noise.

The scan itself never runs the model. Analyze with the model is what does, one pass per file, at most 10 files per request, roughly a minute each. Three output formats are offered: Model summary, Full report and Compressed. Results are stored and listed in the dialog's history.

Fields reference

Connectors table

ColumnShows
NameThe connector's display name
TypeGitHub / GHES, GitLab, Gitea / Forgejo or Generic git (polled)
HostThe base URL, or public host
CredentialsA key fingerprint, token set, or none
Statusactive, disabled, error (tooltip carries the host's message) or no secret

Row actions: Test, Assign repo, Delete.

Tracked repositories table

ColumnShows
RepositoryThe repository name, with its last error underneath in red
BranchThe default branch — the only branch tracked
Baselinepending, running, complete or failed, plus the live progress string
Indexed commitThe first eight characters of the anchor every increment diffs from
Last eventWhen the last tracking event was recorded
EnabledA switch that stops and starts tracking

Row actions: Findings, History, Rebaseline, Stop.

Create-connector fields

FieldTypesRule
TypeAllOne of the four connector types. Cannot be changed afterwards
Display nameAllRequired, up to 255 characters
GitHub Enterprise Server URLGitHubBlank for github.com. Host only, up to 2048 characters
GitLab URL / Gitea / Forgejo URLGitLab, GiteaHost only
Clone URLGeneric gitAn ssh, ssh://, git:// or https:// clone URL
GitHub organizationGitHubBlank creates the App on your own account
App IDGitHub with existing AppUp to 64 characters
App private key (PEM)GitHub with existing AppUp to 16384 characters
Access tokenGitLab, GiteaUp to 4096 characters, stored encrypted and never returned

Verify

  1. The connector's Status reads active after Test, and the toast names a non-zero repository count.
  2. The assigned repository reaches Baseline complete and shows an Indexed commit.
  3. Findings opens and the header names a job id and a decision.
  4. Push a commit to the tracked branch. Within a poll or a webhook delivery, Last event moves and History shows an increment row that ends completed.
  5. This module writes nothing to Event logs. Creating a connector, deleting one, and reassigning a repository to another tenant leave no audit row. The only trail is the History dialog, which the code describes as a troubleshooting log rather than an audit record, and which is pruned after 30 days.

If it fails

  • Refusing to store connector credentials: JWT_SECRET is not set, so they would be written unencrypted. Set a persistent JWT_SECRET and restart. — the appliance is running with an ephemeral secret. Set JWT_SECRET and restart before creating a connector.
  • Leave the host URL blank for github.com — it is only for GitHub Enterprise Server. / Host URL must be the GHES host only (https://…), not a repository URL. — you entered a repository URL where the host was expected.
  • That repository is not readable without credentials, and an https clone URL cannot use a deploy key. Use an ssh URL (git@host:org/repo.git) so a key can be generated, or put a token in the https URL. — a private generic-git repository behind an https clone URL.
  • A clone URL cannot begin with "-" — git would read it as an option. / Clone URL must be ssh (git@host:org/repo.git), ssh://, git:// or https://. — the clone URL failed validation.
  • Repository … is not visible to this connector — the repository is not in what the connector's credentials can list. On the GitHub App path, add it to the installation.
  • No completed scan for this repository yet — check the event log — you opened findings, files or structure before any scan finished. Open History.
  • This repository has no completed baseline yet, so there is no commit to read the files at. — model analysis needs an indexed commit.
  • Select findings in at most 10 files — … were chosen. Each file is a separate model round trip. — narrow the selection.
  • Scan … has expired from the auditor's retention window — rebaseline the repository to scan it again — the auditor pruned the job. Select Rebaseline.
  • Not queued — Code Safety is not licensed on this deployment on Rebaseline — the auditor client is disabled. Check CODE_SAFETY_SERVICE_ENABLED and the licence.
  • Every route on the page returns 404 while the sidebar link is present — REPO_TRACKING_ENABLED is not true. The sidebar hides this page on the licence key only; it has no probe for the feature flag, so a licensed appliance with the flag off shows a page that cannot work.
  • This feature (…) is not included in the installed license tier. Contact your CID representative to upgrade. with FEATURE_NOT_LICENSED — code safety is Enterprise only.
  • This is a read-only demo account (viewer role) — actions and changes are disabled. with READ_ONLY_ROLE — a viewer selected a control that looked live.

Known gaps

  • No audit trail. Nothing this page does is written to the event log.
  • No connector editing or credential rotation in the UI, despite the module README describing rotation.
  • No report or SARIF download. Both exist in the API; neither has a button.
  • Findings are not reconciled between scans. Each job reports independently, so CID222 cannot yet say which findings are new since last week.
  • Most of this page is untranslated. Only the page title, subtitle and the access-denied message go through the translation files; every dialog, table header, tag and toast is hard-coded English, including on a Turkish appliance.
  • Neither list is paginated. Both tables return every row.

Next steps

  • Access control — why this page is superadmin-only and what a viewer may read.
  • License — confirm the appliance carries the code-safety capability.
  • Claude Code — the other code-safety surface: what developers using the coding assistant did, and which filters fired.

Last updated on

On this page

Download PDF