Capabilities · 13. Reaching it

Two surfaces do the work: HTTP on 14800 and MCP on 14801. The gRPC plane on 4007 binds, answers, and every RPC on it returns unimplemented. If you take one thing from this chapter, take that, because it is the claim most likely to cost you a week.

This chapter is explanation. It says what each surface is for, why it is shaped the way it is, and where it stops. Route tables are in the API reference and tool schemas are in api/11; nothing here reproduces them.

13.0 In plain English

The daemon can be reached six ways, and they are not equivalent. Choosing wrongly is the most expensive mistake in this chapter, so here is the short version before the detail.

If you are writing an integration, use HTTP. It is the largest surface, it is the one every other surface is expressed in terms of, and it is complete. If you are wiring up an agent, use MCP, which is the same capabilities packaged as tools an agent can discover and call. If you are an operator at a terminal, use corecruxctl. If you are running Claude Code, install the hooks, because they are the piece that makes coordination leases and configuration drift warnings actually reach the agent rather than sitting in the daemon unread. If you want a typed client in TypeScript or Python, the SDKs exist and carry a published defect register you should read first.

Do not use gRPC. Port 4007 opens, it speaks gRPC properly, its authorisation checks are real and run before anything else, and then every single remote procedure returns unimplemented. This is not a bug and it will not be fixed by configuration: the data plane behind it belongs to a different distribution. The port binding is the confusing part, because a port that accepts connections looks like a port that works. Section 13.4 gives it in full.

One more thing that catches people. MCP is a large tool surface, 118 tools, over a very small protocol surface: exactly four JSON-RPC methods. If your client expects the MCP resources or prompts features, it will not find them, and it will not find them in a way that returns HTTP 200 with an error buried in the body. Section 13.2 explains why, and what your client should check instead.

13.1 The HTTP API

Status SHIPPED · Reached through 303 paths across 347 registrations, most under a single /v1 prefix; GET /v1/openapi.json · Who it is for both

What it does. The HTTP API is the complete surface. Errors are RFC 9457 problem bodies carrying a real code vocabulary rather than prose, correlation headers appear on every response, and the daemon serves a self-describing manifest so a client can enumerate what this deployment actually exposes.

Why it works this way. A machine-readable error code was chosen over relying on the status code because HTTP statuses are too coarse to distinguish "you asked for a tenant you may not see" from "that feature is not enabled here", and clients that branch on prose break the first time the prose improves. Correlation headers on every response, rather than only on errors, exist so that the log line for a slow success is findable, which is the case you actually need during an investigation. The manifest is served by the daemon rather than published as a static document because a deployment's real surface depends on its flags.

What changes for you.

  • As an operator: you can enumerate a deployment's surface from the deployment, so "what does this box expose" is a request rather than an audit.
  • As an agent: every failure carries a stable code, so error handling can be written once.

What it does not do.

  • /v1/openapi.json is not a complete OpenAPI document. It carries request and response schemas for roughly 7% of the surface (openapi.rs:499), so code generated from it will cover a small fraction of what you can call. Treat it as discovery, not as a contract source.
  • Route presence is not capability. Several routes stay mounted when their feature is disabled and answer with an explicit disabled or 402 response rather than disappearing, so a 404 sweep is not a feature inventory.
  • The problem body's code is stable; its detail is not. Do not parse detail.

Where the detail lives. Every route, grouped by plane: api/00 API index. The error vocabulary and status mapping: daemon/08 Errors.

13.2 The MCP server

Status SHIPPED; the A2A agent card is FLAG CRUX_AGENT_CARD, default on; RFC 9728 resource metadata returns 404 unless CRUX_MCP_RESOURCE_URL is set · Reached through POST /mcp, GET /mcp, /.well-known/agent-card, /.well-known/oauth-protected-resource, corecruxd mcp-stdio · Who it is for agent

What it does. 118 tools are served over Streamable HTTP on a dedicated port, with an SSE channel for the one server-to-client notification the daemon sends, notifications/tools/list_changed. A stdio bridge ships inside the same binary, so a client that needs stdio gets it without a separate shim. An A2A agent card is served by default for discovery, and RFC 9728 protected-resource metadata is available once you tell the daemon its own external URL.

Why it works this way. Shipping the stdio bridge in the daemon binary rather than as a separate npm or pip package is a deliberate rejection of the usual arrangement, where a thin shim drifts a version behind the server and produces bug reports nobody can reproduce. One binary means the bridge and the server are the same build, always. The tool surface is large because the daemon's capabilities are large, and rather than curating it down for everyone, the shaping mechanism in §13.3 lets each deployment decide.

What changes for you.

  • As an agent: every capability that has a tool is discoverable through tools/list, and the agent card gives you a machine-readable identity for the daemon before you connect.
  • As an operator: MCP is a separate listener on a separate port, so you can expose HTTP to a network and keep the agent plane on loopback.

What it does not do.

  • It implements exactly four JSON-RPC methods: initialize, notifications/initialized, tools/list and tools/call. Everything else hits one fallback (dispatch.rs:460). There is no resources/list, resources/read, prompts/list, prompts/get, completion/complete, logging/setLevel, roots/* or ping handler anywhere in the server.
  • Those unsupported methods return JSON-RPC -32601 inside an HTTP 200. A client that checks the transport status and not the body will read a refusal as a success. initialize advertises only {"tools": {"listChanged": true}}, so a spec-compliant client will not call them in the first place; the hazard is bespoke clients that assume.
  • token_budget is not the contract the house conventions imply. Of the 27 tools that accept it, only 7 reject a call that omits it. Six silently substitute a hardcoded 500 or 2000 that no environment variable overrides, and 14 return unbounded results, including all three core retrieval tools: query, query_scan and query_facts (query.rs:118). There is no server-side clamp anywhere on the MCP surface. Pass a budget on every retrieval call because omitting it is unbounded, not because the server will stop you.
  • Anonymous access is permitted only when the agent token registry is empty. A registry with an invalid entry refuses the boot rather than degrading to anonymous.

Where the detail lives. Protocol handshake, tool catalogue and the error model: api/11 MCP overview.

13.3 Tool-surface shaping

Status FLAG CORECRUXD_TOOL_SURFACE, default full; accepted values full, minimal, dynamic · Reached through MCP tools/list; intent is declared via cuecrux_session(intent) · Who it is for agent

What it does. Behind CORECRUXD_TOOL_SURFACE, default full, the advertised tool list can be narrowed to a core floor (minimal) or to that floor plus the top-scoring tools for a declared intent blended with the trace ring (dynamic). Tools shaped out of the listing remain callable by name.

Why it works this way. Handing an agent 118 tools costs context on every turn and measurably degrades tool choice, but hiding tools from an agent that legitimately needs one is worse. Keeping shaped-out tools callable resolves that: the list is a recommendation, not a permission boundary, so a narrowed surface can never make a capability unreachable. dynamic blends a declared intent with observed behaviour because an intent alone is a guess and behaviour alone cannot anticipate a new task. An unrecognised value falls back to full, so a typo can never silently shrink an agent's surface.

What changes for you.

  • As an agent: declaring an intent at session open changes what you are offered on the next listing, not the current one, because stateless HTTP cannot push a list change without an open stream.
  • As an operator: minimal is a context-cost decision, and it is reversible without restarting anything that holds state.

What it does not do.

  • Shaping is not authorisation. A shaped-out tool is still callable by name, and access control is the capability and scope machinery, not this.
  • Extension tools have no affinity score, so they are invisible under minimal and generally under dynamic too.
  • The change lands on the next tools/list. A client that caches its tool list will not see it until it refreshes.

Where the detail lives. Modes, scoring inputs and the core floor: api/11 MCP overview. How shaping interacts with extension tools: extending/10 The MCP surface.

13.4 The gRPC data plane

Status DECLARED-NOT-IMPLEMENTED in this edition · Reached through port 4007, services CoreCruxDataPlaneV1 and CoreCruxExportV1 · Who it is for human

What it does. Two services are registered on port 4007 with transport hardening: TCP nodelay, HTTP/2 keepalives, a concurrent-stream cap and panic recovery. Ten RPCs are registered across them. Every one of the ten returns Status::unimplemented("requires the proprietary edition"), including the primary write path AppendBatch (grpc.rs:765) and ReadStream. A third service, CoreCruxObserveV1, is generated from its proto file with five RPCs, has no implementation anywhere in the workspace, and is never added to the server (grpc.rs:979). Calling one of its operations gets a transport-level unimplemented for an unknown service.

Why it works this way. The Community Edition and the dataplane-enabled distribution share one binary layout, and the skeleton is kept so that the two builds differ in implementation rather than in shape. The authorisation checks are genuinely wired: AppendBatch verifies the events:write scope for the tenant and only then refuses, which is why the plane is useful for testing an auth configuration and useless for moving data. Keeping the listener rather than removing it was a distribution decision, and the documentation cost of that decision is this section.

What changes for you.

  • As an operator: port 4007 will pass a connectivity check, a TLS check and an authorisation check while being incapable of doing any work. Do not use it as a health signal, and expect a bind failure on 4007 to take down your HTTP plane with it.
  • As an agent: there is nothing here. Everything the observe service was going to offer is available over HTTP at /v1/ops/facts, /v1/ops/errors, /v1/ops/health, /v1/bootstrap/pull and /v1/bootstrap/status.

What it does not do.

  • No RPC on 4007 does anything in this edition. Not append, not read, not replay, not export. There is no flag, licence key or configuration that changes this; the data plane belongs to a different distribution.
  • CoreCruxObserveV1 is not merely unimplemented, it is not served. Its five operations fail differently from the other ten, as an unknown service.
  • The compiled corecruxctl ops append client targets AppendBatch and is wired to no CLI subcommand, which is consistent: it has nothing to talk to.

Where the detail lives. Proto definitions, service shapes and what each RPC would do in a dataplane edition: api/10 gRPC.

13.5 corecruxctl, the operator CLI

Status SHIPPED · Reached through the corecruxctl binary, 50 modules · Who it is for human

What it does. corecruxctl is the operator's surface: deployment preflight, receipts and audit packs, offline verification, ingest, memory packs, code intelligence, benchmark and parity drivers, machine registration and cross-machine session sharing. Two entry points are documented in-code as the on-ramps: corecruxctl start and corecruxctl deploy-audit.

Why it works this way. Several of these capabilities have no HTTP equivalent and should not have one, because they operate on the daemon's files rather than through the daemon: offline bundle verification is worthless if it needs the daemon it is verifying to be running. Putting them in a CLI rather than an admin route is what makes them usable during exactly the incident they were built for, which is the one where the daemon is not answering.

What changes for you.

  • As an operator: verification, export and preflight work with the daemon down, which is when you need them.
  • As an agent: almost nothing here is agent-facing. The CLI assumes a person and a shell.

What it does not do.

  • corecruxctl ops append is compiled but wired to no subcommand. It is not a command; do not build a runbook around it.
  • Twelve further capabilities in this binary have no coverage anywhere in the published documentation. They are enumerated in §13.6 rather than left to be discovered.
  • The CLI is not a remote client for everything. Some subcommands act on local files and are meaningless against a remote daemon.

Where the detail lives. start, deploy-audit and the operational subcommands: daemon/17 Operations. Running the CLI against a local build: contributing/03 Running locally.

13.6 The undocumented corecruxctl capability set

Status SHIPPED, and documented nowhere else · Reached through corecruxctl subcommands · Who it is for human

What it does. Twelve distinct operator capabilities ship in corecruxctl and have no section in any of the 82 published reference chapters. This explainer exists to make them findable. Three of them, parity, reconcile and snapshot, are exactly the tools you want during an incident; three more are the multi-machine story; one is the honest path for feeding the cost lens.

SubcommandWhat it is for
parityCompare two daemons and assert that the same reads return byte-equivalent results
reconcileCompare on-disk state against what the manifest says should be there
snapshotOpen and print a .ccxs projection snapshot
stage1-importBulk-load legacy event streams with deterministic segment ids
machineRegister this machine with the daemon
configSave this machine's Claude Code configuration to the daemon
sessionShare session state across machines
observe ingestIngest an agent transcript, the honest input path for cost attribution
compaction-syncCoordinate compaction against a synchronised peer
tooling-envReport the toolchain environment the CLI resolved
audit-packProduce a packaged audit artefact
openclawImport a foreign agent-memory workspace

Why it works this way. These accumulated as operational needs arose rather than as a designed suite, which is why they are uneven in scope and why the reference set never grew a home for them. Listing them here rather than quietly writing twelve reference sections is the honest move: this page tells you they exist and what each is for, and the authoritative usage is the binary's own --help, which is generated from the code and therefore cannot drift from it.

What changes for you.

  • As an operator: during an incident, parity answers "are these two daemons returning the same thing" and reconcile answers "does the disk match the manifest", without either question needing a support ticket.
  • As an agent: nothing. This set is entirely operator-facing.

What it does not do.

  • None of these has a published reference page, a documented flag set or a stability guarantee in the published sets. Run --help for the current interface and expect it to change without a documentation entry.
  • observe ingest reads a transcript you supply. It does not capture anything on its own.
  • openclaw imports a foreign workspace. Import is not a round trip, and nothing here promises the export side.

Where the detail lives. Nothing in the published reference sets covers these twelve; that gap is recorded deliberately. The nearest context is the operator surface in daemon/17 Operations. Source: corecruxctl parity module.

13.7 Claude Code hooks

Status SHIPPED · Reached through the crux-hook binary, installed by crux-config-wizard init or corecruxctl hooks install · Who it is for both

What it does. A client-side hook binary runs three things: the SessionStart boot banner, the configuration-audit advisory, and the PreToolUse punchcard check. It is the component that makes coordination leases and drift warnings actually reach an agent.

Why it works this way. The daemon can compute that two sessions are about to edit the same subtree, but it has no way to tell an agent so, because the daemon is not in the agent's loop. The hook is. This is also the honest boundary on path leases: in enforce mode the daemon refuses to grant a conflicting lease, and the refusal of the actual edit is performed here, on the client. A cooperating client is protected; a non-cooperating one is not, and that is a property of the architecture rather than a gap to be closed later.

What changes for you.

  • As an operator: installing the hook is what turns coordination from a daemon feature into something your team experiences.
  • As an agent: your boot banner, your live-session peers and your configuration-drift warning all arrive through this path.

What it does not do.

  • It cannot stop a client that does not run it. Lease enforcement is client-side, so an agent without the hook edits whatever it likes.
  • It is advisory throughout. Overlap warnings and intents never block, by design.
  • It runs in the client's process, not the daemon's. A daemon that is unreachable produces a hook that fails open and stays quiet.

Where the detail lives. Installation and the wizard's role: daemon/02 The config wizard. What the banner, the punchcard check and the audit actually do: daemon/15 Coordination and cost.

13.8 crux-llm-shim

Status SHIPPED, experimental v1, default off; requires CRUX_LLM_SHIM=1 · Reached through the crux-llm-shim binary, shipped in releases · Who it is for agent

What it does. crux-llm-shim is an opt-in local proxy that sits between an OpenAI-compatible client and its upstream. Every chat/completions request passing through it gets the Crux context bundle prepended as the first system message, and mediation receipt records are minted per request and per stream end-state. Everything else passes through unmodified. It is default off and does nothing until CRUX_LLM_SHIM=1 is set.

Why it works this way. Context injection through MCP requires the harness to speak MCP. A proxy requires only that the harness speaks the OpenAI wire format, which is the one thing nearly every local runner and client already does. Putting it in the hooks crate rather than in the daemon was a build decision, keeping it to zero new dependencies. Minting mediation receipts at the proxy is the interesting part: the proxy is the only component that observes both the request and how the stream ended, so it is the only place that record can honestly be made.

What changes for you.

  • As an agent: if your harness is not MCP-aware, point it at the shim's listen address and it gets Crux context anyway.
  • As an operator: it is a separate process you run deliberately, not a daemon feature you enable.

What it does not do.

  • It is experimental v1 and default off. It is not part of the supported integration path, and no published chapter covers it.
  • It enforces an upstream allowlist. It is not a general-purpose proxy and will not forward to an arbitrary endpoint.
  • A mediation receipt records that a model call happened and how the stream ended. It does not attest to what the model did with the context.

Turn it on. Set CRUX_LLM_SHIM=1 and run the binary with --upstream, --listen and --bundle-file.

Where the detail lives. No published chapter documents this binary; it is named as a release artefact in daemon/03 Crate map. Behaviour and flags: crux_llm_shim.rs:9.

13.9 Official SDKs

Status SHIPPED, with a dated defect register: 6 blocking, 5 staleness · Reached through npm @cuecrux/client, PyPI corecrux-client · Who it is for both

What it does. A TypeScript client and a Python client wrap the HTTP API with typed request and response shapes, a full method inventory and a shared error model. A second SDKCrux estate publishes into the same npm scope and is documented as it is rather than as it was intended to be.

Why it works this way. The SDKs exist so that the common paths are typed and the error handling is written once, and they are documented alongside a defect register rather than being presented as finished. That choice is the point: an SDK method that is broken is worse than an absent one, because the absence is discoverable and the breakage is not until runtime. Publishing the register means you can decide, before you depend on a method, whether to use it or to call the route directly.

What changes for you.

  • As an operator: the SDK is not the only client. Anything an SDK cannot do, the HTTP API can, and the SDK is a convenience over that.
  • As an agent: if you are running inside a TypeScript or Python harness, the typed client removes a layer of hand-written request building.

What it does not do.

  • Six known defects are blocking. Read the register before depending on a method, because the list is dated and specific rather than a general disclaimer.
  • SDK coverage trails the HTTP surface. A route with no SDK method is not a route that does not exist.
  • The SDKCrux packages share the npm scope but are a separate estate. Do not assume one estate's guarantees apply to the other.

Where the detail lives. Method inventories, authentication and the error model: sdk/00 SDK index. The dated defect register: sdk/06 Known issues.

13.10 The desktop shell

Status NOT IN THE DEFAULT BUILD; excluded from the cargo workspace and built only by a dedicated CI workflow · Reached through nothing at runtime · Who it is for human

What it does. Two crates under shells/desktop, a Tauri v2 application and a standard-library-only lifecycle supervisor, are excluded from the cargo workspace (Cargo.toml:37) and compiled only by a dedicated GitHub Actions workflow. That workflow has no release or deploy steps, and its compile job is a required status check on every pull request.

Why it works this way. The desktop application pulls the webkit2gtk and GTK stack, which cannot build on the project's headless self-hosted CI runners or on a WSL development box, so including it in the workspace would break cargo build for every contributor. Excluding it and gating it on a GitHub-hosted runner keeps the main build clean while still proving the code compiles on every change. The compile gate is required rather than optional so the shell cannot rot silently between releases.

What changes for you.

  • As an operator: there is nothing to install and nothing to run. The daemon's operator surface is the built-in console in §12.12 plus corecruxctl.
  • As an agent: nothing. The desktop shell is not a runtime surface.

What it does not do.

  • It is not a shipped product surface in this edition. It is not in any release artefact, it is not in the default build, and a CI compile gate is not a support commitment.
  • Excluding it from the workspace means cargo build at the repository root does not build it. Building it requires the Tauri Linux prerequisites, which the workflow installs itself.
  • No published chapter documents it as a capability. contributing/02 explains only why it is excluded from the workspace.

Where the detail lives. Why the exclusion exists and where the crates sit: contributing/02 Repository tour.


Each explainer above carries its own grounding in its final block. Statuses in this chapter reconcile against the daemon feature inventory taken at 93b41a7d9735c4f7d1186c7a57b861d746273366; the complete status table for all 119 capabilities is chapter 15.