Code
Package code is search and symbols across your repos, for you and your agents.
Package code is search and symbols across your repos, for you and your agents.
| Base URL | https://api.hanzo.ai |
| Operations | 7 |
| Auth | Authorization: Bearer $HANZO_API_KEY |
Specification
HIP-1114 · Code — Search and Symbols — Draft · read the specification →
/v1/code is search and symbols across an org's repositories, for people and
for their agents: a per-org code-intelligence engine implemented in
hanzoai/cloud apps/code. Retrieval is hybrid — lexical, symbolic and
semantic tiers fused with reciprocal-rank fusion — because embeddings alone
under-serve code search (apps/code/code.go:3-6). This HIP states the three
tiers, the physical org boundary of the store, and how the one paid dependency
(embeddings) is attributed.
Motivation
An agent editing code needs three different questions answered well: "where does this string appear" (exact, including operators and case conventions), "where is this symbol defined and used" (structural), and "what code means this" (semantic). Each tier serves one of them and fails at the others; a single-tier engine forces every question through the wrong index.
Specification
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.
§1 The three tiers
- lexical — FTS5 trigram over code-tokenized text: camelCase and
snake_case split, operators kept; substring and regex in the Zoekt model
(
apps/code/store.go,tokenize.go). - symbolic —
go/parserfor Go, with real def→ref edges; compact lexical extractors for TS/JS, Python, Rust and Solidity (apps/code/parse.go). - semantic — AST-boundary chunks embedded through the same gateway
/embeddingsthe knowledge plane uses, ranked by cosine over a float32 vector table; the schema is kept compatible with asqlite-vecvec0KNN as a future seam, not a linked dependency today (apps/code/store.go:561).
hybrid fuses the tiers; a caller may also pin one with type=.
§2 The store
One SQLite file per org at {DataDir}/orgs/{slug}/code.db (HIP-0302): the org
boundary is physical, so a query in one org's file can never reach another
org's rows (apps/code/code.go:17-19). Stores open lazily through the shared
cloud.OrgStore cache, and storeFor is the one way the package reaches one
(apps/code/code.go:91, code.go:141-150). Bounds cap what one request can
amplify into the shared file or the gateway (apps/code/code.go:47-58).
§3 The addresses
Everything is under /v1/code: search, context (a budget-packed context
bundle), ask (a cited RAG answer), index (incremental, with prune), file
and tree. All operations are typed
(apps/code/typed_wire_test.go). /v1/code/lsp is NOT this capability: the
live language server is its own capability routed by prefix specificity
(manifest/apps.go:315,325), folded under this address because static index
and live server are two reads of one repository.
§4 Tenancy and money
Every request resolves its org through principal.Org (HIP-0026): no validated
principal, 403; a client X-Org-Id is never trusted. The surface itself is
free (plugin/code/main.go:21, cloud.Free). Its embedding calls are billed
where inference is always billed — on the AI plane — attributed to
principal.Ledger and the server-minted project, neither of which may be an
input field (apps/code/code.go:176-189). Off the HTTP path both are empty,
which is the unbilled default — and principal.Acting has already refused
before any op runs.
§5 Events, telemetry, stage, upstreams
It publishes no events on the bus and emits nothing to observability beyond the
request span. Its stage is ga: the manifest row (manifest/apps.go:315)
declares no stage, and absent means ga. It embeds no third-party engine: parsing is the Go
standard library plus this package's own extractors, storage is the
hanzoai/sqlite facade, and the lexical design follows the trigram model Zoekt
demonstrated without importing it.
Rationale
The alternative to per-org files is one index with an org column — cheaper to operate, and one forgotten predicate away from serving one tenant's source to another. Source code is the asset tenants trust the platform with least willingly, so the boundary is physical and the cost (an open file per active org, amortized by the store cache) is accepted.
The alternative to fusing three tiers is picking one. Embeddings-only misses
exact identifiers; lexical-only cannot answer "what code does this"; fusion is
the measured lesson of code retrieval and each tier stays independently
testable (apps/code/search.go).
Security Considerations
The store is an index of private source. The wrong implementation leaks it two
ways: across tenants (closed physically, §2) or through the paid seam — an
attacker who can set the billing org or project on an embedding call can charge
inference to a victim, which is why payer and project come only from validated,
server-minted values (§4). ask answers only from the caller's own org's
index, so the RAG surface cannot become a cross-tenant oracle.
Four surfaces
| Surface | Reaches this capability as | Coverage |
|---|---|---|
| REST | code at its own prefix | 7 operations |
| CLI | hanzo code … | 7 of 7 |
| SDK | CodeApi in every published client | 7 methods |
| MCP | tool code on https://api.hanzo.ai/v1/mcp | 7 operations, 4 under the document's own id — ask describe for the rest |
Quickstart
export HANZO_API_KEY=sk-... # console.hanzo.ai → API keysThen the first call — a read that needs nothing but the key. GET /v1/code/ask, operation get_code_ask:
hanzo code ask getimport { Configuration, CodeApi } from 'hanzoai';
const api = new CodeApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.getCodeAsk();from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import CodeApi
client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = CodeApi(client).get_code_ask()cfg := cloud.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := cloud.NewAPIClient(cfg)
resp, _, err := client.CodeAPI.GetCodeAsk(context.Background()).Execute()
if err != nil {
return err
}use hanzo_cloud::apis::{configuration::Configuration, code_api};
let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();
let result = code_api::get_code_ask(&cfg, Default::default()).await?;import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.CodeApi;
ApiClient client = new ApiClient();
client.setRequestInterceptor(b -> b.header("Authorization", "Bearer " + System.getenv("HANZO_API_KEY")));
var result = new CodeApi(client).getCodeAsk();curl https://api.hanzo.ai/v1/code/ask \
-H "Authorization: Bearer $HANZO_API_KEY"Tool code, op get_code_ask — POST the JSON-RPC envelope to https://api.hanzo.ai/v1/mcp.
curl -X POST https://api.hanzo.ai/v1/mcp \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "code",
"arguments": {
"op": "get_code_ask",
"input": {}
}
}
}'Answers 200 with object — ok.
Endpoints
| Endpoint | What it does |
|---|---|
GET /v1/code/ask | Answers a question about the caller org's code with a CITED answer: retrieval packs grounding context, then the synthesizer writes the answer over… |
POST /v1/code/ask | Is askGet with the question in the request BODY, for a question too long or too awkward to put in a URL. |
POST /v1/code/context | Packs the most relevant code for a query into a token budget — THE primitive for a coding agent that has to decide what to put in a prompt. |
GET /v1/code/file | Returns the INDEXED content of one file — read_file over the chunks the search tiers hold, for pulling up code an agent just found. |
POST /v1/code/index | (re)indexes a repository for the caller's org, incrementally: files whose content hash is unchanged are skipped, so re-sending a whole tree is cheap. |
GET /v1/code/search | Finds code in the caller org's index across three orthogonal retrieval tiers fused by reciprocal-rank fusion: lexical (FTS5 trigram over… |
GET /v1/code/tree | Returns one repository's file structure with a per-file symbol count — get_repo_structure over the org's own index, with no git checkout involved. |
How is this guide?