Code index
The code index lets people and agents search and read code: your own repositories, and the open-source ones your platform runs on, each at the commit you actually run. Every hit, file and diff it returns is cited as repo@commit:path:L<start>-L<end>, so an answer built on it can say exactly where each claim comes from.
It is what the architect AgentRoles read through when they hydrate an issue, answer a question about a Product, draft issues or review a Release. With it they ground a claim about how Argo CD syncs, or what changed in a library between two versions, in that code at the version the install runs, instead of in memory.
Every install can run one. It is off until you turn it on.
What it holds
The index reads a knowledge repository, a git repository you keep, with two things in it:
| Path | What it is |
|---|---|
manifest/repos.yaml | The manifest: every repository the index holds, each with one or more refs, a name for a commit, such as deployed (what you run), latest (the newest upstream release) or main |
cards/<owner>/<name>.md | Repo cards: one short page per repository, written for a reader who has never seen it, describing it at one commit. Optional, and the best start for an agent new to a repository |
For each repository the index keeps a mirror of the history its refs reach and builds a search index of every ref with Zoekt, including symbol definitions. Mirrors and the index are a cache on the index's own disk: they are rebuilt from upstream when it starts and are never backed up. Your record is the knowledge repository.
The index leaves out of every repository:
- vendored and third-party trees (
vendor/,node_modules/,third_party/); - generated code, lock files and minified files;
- binaries, images, archives and media, by name;
- files over 1 MiB, which it lists by name only.
A repository marked vendored: true keeps those trees. A path listed under a repository's hide is never indexed, read, listed or diffed: it answers as if it weren't there.
Turn it on
The code index is part of the chart. Set it in your install's values:
codeIndex:
enabled: true
knowledge:
url: https://github.com/example-org/knowledge.git
ref: main # a branch, tag or commit; empty means main
The gitops template then runs the index in the namespace code-index. Only infrared-api's pods can reach it; infrared-api decides who reads what.
Private repositories
Without a credential the index reads public repositories only, and a private knowledge repository is out of its reach too. To read private ones, give it a GitHub App whose installation can read them. The index mints short-lived, read-only installation tokens from it. Pass the App at install:
helm install ... \
--set-file platformTokens.codeIndexGithubAppId=<(command that prints the app ID) \
--set-file platformTokens.codeIndexGithubAppInstallationId=<(command that prints the installation ID) \
--set-file platformTokens.codeIndexGithubAppPrivateKey=<(command that prints the private key)
The three go together. The App needs only Contents: read on the repositories in your manifest and on the knowledge repository itself.
The record and the App are written once, at install, into the infrared-platform-tokens Secret, so a restore brings the index back with both. To change either later, change that Secret's code-index-* keys: the change reaches the index within the hour. The index reads its record again every hour and its credential at every use.
While it builds its first index, the index isn't ready yet; a code tool answers with an error until it is. A repository the credential can't read is skipped, not fatal.
Write the manifest
version: 1
platform: production # a name for what this record describes
repos:
- name: storefront # lowercase; how tools and citations name it
url: https://github.com/example-org/storefront.git
visibility: internal # public, or internal for a private repository
domain: product # a free-form grouping, shown by code_repos
refs:
- ref: deployed # the first ref is the default
commit: 3f9c2d7e1b4a8c6f0e2d5b7a9c1e3f5d7b9a1c3e
pin: the image tag production runs
- ref: main
commit: 8a1b3c5d7e9f0a2b4c6d8e0f1a3b5c7d9e1f3a5b
track: main # follow the branch: re-resolved every 15 minutes
- name: argo-cd
url: https://github.com/argoproj/argo-cd.git
visibility: public
domain: gitops
refs:
- ref: deployed
commit: c9c369efcc5b2a0bd720803f8d14a1c3eaddf579
tag: v3.5.3
- name: knowledge
url: https://github.com/example-org/knowledge.git
visibility: internal
domain: docs
hide: [eval] # never served
refs:
- ref: main
commit: 0d2e4f6a8b0c2e4f6a8b0c2d4e6f8a0b2c4d6e8f
track: main
Every ref names a full 40-character commit. A ref with track follows that branch: the index resolves it again every 15 minutes, and the commit is only where it starts. A ref without track stays at its commit until you change the manifest. That is what you want for deployed, so answers describe the code you run, not the code on main. tag and pin are notes for people; the index ignores them.
exclude, a list of regular expressions over paths, replaces the default list above when you set it.
Keep deployed current when you upgrade: a script in your release process that writes the new commits is the usual way. The index never guesses which commit you run.
Write repo cards
A card is Markdown with front matter naming the commit it describes:
---
title: "storefront"
repo: example-org/storefront
commit: 3f9c2d7e1b4a8c6f0e2d5b7a9c1e3f5d7b9a1c3e
status: approved
---
The web shop. A Next.js app served by one Deployment per zone; checkout calls
the payments service over gRPC (`src/checkout/client.ts`). ...
code_card returns a repository's card and says it is stale when the ref's commit has moved past the card's commit. A card can also list companions, other repositories at their own commits that it describes too; a companion without a card of its own gets this one.
A card is worth writing for a repository an agent will read often: what it is, its entry points, the few files that matter, and where it is easy to go wrong. Agents can draft one with the architect; have a person review it before it lands in cards/.
Use it
From Claude Code
infrared-mcp serves the eight code_ tools on its code listener, port 8081 of the infrared-mcp Service. Infrared's public /mcp route doesn't serve them, so private code never sits behind the public address. Reach the code listener through a port-forward:
kubectl --context <cluster> -n infrared port-forward svc/infrared-mcp 18090:8081
claude mcp add --transport http infrared http://127.0.0.1:18090/mcp \
--header "Authorization: Bearer $(kubectl --context <cluster> -n infrared get secret infrared-mcp-access -o jsonpath='{.data.token}' | base64 -d)"
The listener serves every other Infrared tool too, so this one server is all Claude Code needs. See Connect an MCP client for the tools' arguments.
An architect in your own sessions
The architect AgentRoles' instructions work as well in a person's own Claude Code as in a run. Copy Ask the architect's mission, responsibilities and guardrails into a Claude Code subagent, .claude/agents/architect.md, and give it only read tools and the code tools:
---
name: architect
description: Answers questions about how our systems work, citing every claim as repo@commit:path:lines. Read-only.
tools: Read, Grep, Glob, mcp__infrared__code_repos, mcp__infrared__code_card, mcp__infrared__code_search, mcp__infrared__code_symbol, mcp__infrared__code_read, mcp__infrared__code_tree, mcp__infrared__code_log, mcp__infrared__code_diff
mcpServers:
- infrared
---
You are the architect. ...
Then ask it things like "why does a sync wave wait on a Job, at the Argo CD we run?" or "what changed in the gateway between the version we run and the latest release that touches TLS?". It starts with code_card, searches with code_search and code_symbol, reads with code_read, and answers with a source list of exact commits.
In agent runs
A step whose AgentRole lists infrared under mcpServers gets the code tools through its runner. The catalog's four architect roles do. Nothing else is needed: once the index is on, those steps start citing code, and without it they run as before, since no step needs the tools to finish. The code tools only read, and the step reaches them only through its runner, never with a credential of its own.
Through the API
The same reads are API operations under /v1/orgs/{org}/code/: repos, search, symbols, file, tree, log, diff and card. They take an API token of anyone with a role in the org. See the API reference.
Who can read it
- On your own install, the code index belongs to the install's platform org, the org you created in the setup wizard. Anyone with a role in that org, and that org's agent steps, read every repository in the manifest.
- Other orgs on the same install don't read it yet: their API calls answer
403, and their steps run without the code tools. An install has one manifest. Per-org manifests come with API tokens that can be limited to one org. - A repository the manifest doesn't list is never readable, whatever a query asks: every search is limited to the repositories the caller may read before it reaches the index, and an answer naming any other repository is refused whole.
- Logs and diffs show an author's GitHub login only, never a name or email address.
What the tools return is text from the repositories. Agents treat it as data, never as instructions.
Limits
| Limit | |
|---|---|
| A file read | 2,000 lines and 512 KiB per call |
| A diff | 1 MiB, then cut |
| A log | 200 commits per call |
| Search and symbol hits | 200 per call; 20 by default |
| A file in the index | 1 MiB; larger files are listed by name only |
| A tool's answer through MCP | 100,000 bytes, then cut, and it says so |
The index runs as one pod, the code service beside Zoekt, with its cache on the pod's own disk. The cache is rebuilt from upstream on every start, so after a restart the code tools answer only once the first index is built again, which takes longer the more and larger the repositories in the manifest.