Skip to main content
Meerkat is a large Rust workspace. The featured install paths are prebuilt: the Homebrew tap, the GitHub Release archives, and the SDKs’ automatic rkat-rpc download. This page is for everyone else: people who run cargo install rkat, embed the crates in their own binary, build inside a container, or need to know where the prebuilt binaries will and will not run. Every number below was measured against this repository; the memory and time figures come from one cold build described in Build Cost.

What A Build Compiles

The workspace has ~50 member crates. Its lockfile resolves ~670 package entries (~600 distinct crates) for the whole workspace with every feature, dev-dependency, and optional backend enabled. A default-feature cargo install rkat compiles ~380 crates. Most of those are ordinary. The cost is concentrated in a few very large first-party crates, each of which is one rustc and LLVM invocation: These four, plus the final rkat crate that pulls the whole surface into one binary, dominate both wall time and peak memory, which is why the release lane and the rest of this page talk about per-package opt-level pins rather than about the dependency count. Toolchain and native requirements:
  • The repository pins Rust 1.94.1 in rust-toolchain.toml; make install-build-deps installs exactly that toolchain into a checkout. The published crates declare rust-version = "1.94.0", so cargo install rkat needs Rust 1.94 or newer.
  • The default feature set needs a C compiler and libc headers (bundled SQLite and a few other C sources compile through the cc crate) and nothing else. TLS is rustls; no OpenSSL development package is required and the release gate rejects binaries that link one.
  • cmake is needed only by the optional live-webrtc feature of the meerkat facade and meerkat-rpc crates (meerkat-live webrtc, bundled Opus through audiopus_sys). rkat has no such feature, and no shipped binary enables it by default, so a cargo install of any of them never compiles it.

Build Cost

Two cold builds, measured for this page:
Both ran from an empty target directory with default features and Rust 1.94.1 on an 18-core Apple M5 Max with 128 GiB of RAM, so nothing was memory-constrained and Cargo ran 18 jobs in parallel. The first used Cargo’s default release profile; the second used the low-memory profile below (opt-level = 1 globally, opt-level = 0 for meerkat-machine-schema), with the same job count so the per-crate peaks are comparable. Peaks were sampled once per second from ps. How to translate that to your host:
  • The largest single crate is the floor, and only opt-level = 0 moves it. Even with CARGO_BUILD_JOBS=1, the build needs as much memory as its biggest rustc process. In both runs above that was meerkat-mob at ~11 GiB: going from opt-level = 3 to 1 halved the aggregate peak, the rkat crate, and meerkat-machine-schema, but left meerkat-mob where it was. A third run (the same rkat build with the low-memory profile plus meerkat-mob pinned to opt-level = 0) brought meerkat-mob down to ~6.3 GiB, after which the largest process is meerkat-runtime at ~7.4 GiB. Below that, the levers are the feature set (dropping mob removes the crate), swap, and the prebuilt binaries.
  • Aggregate memory scales with the job count. Cargo runs up to one rustc per core. The aggregate peaks above happened while four or five large first-party crates were compiling at once; with N jobs, budget for the N largest crates that can be in flight together. meerkat-mob, meerkat-runtime, and meerkat-machine-schema did overlap in the default build, and that overlap produced its peak.
  • Wall time scales with cores only up to the serial tail. The hundreds of small dependencies compile in parallel in the first few minutes, but the large first-party crates depend on each other, and the final rkat crate compiled alone for roughly the last third of the wall time. A 4-core cloud VM should expect a few times the wall time above, and the tail does not shrink with more cores. cargo install also has no warm cache to reuse, so every install is a cold build.
  • Disk is transient for cargo install. The temporary target directory is deleted after the binary is installed, but it must fit while the build runs. A checkout that keeps its target/ needs the full figure per profile.
The project’s own record of what happens on small hosts is in .github/workflows/release.yml: the Windows release lane runs on a 16 GB GitHub-hosted runner with no memory overcommit, and it documents rustc exceeding that runner’s commit limit on the generated machine catalog. That lane therefore builds with CARGO_BUILD_JOBS=2 and pins meerkat-machine-schema to opt-level = 0 and meerkat-runtime and meerkat-mcp to opt-level = 1. The Linux and macOS release binaries keep full opt-level = 3; the pins are Windows-only. Continuous integration (.github/workflows/ci.yml) compiles its broad lane on BuildBuddy remote executors, with only the dense Mob topology job and the nightly lanes on GitHub-hosted runners; release binaries are produced only by the release workflow. Rough sizing, derived from the peaks above:

Lowering Memory

Where profile settings come from

Cargo reads [profile.*] tables only from the root manifest of the build it is running. Two consequences matter here:
  • The Meerkat workspace ships no [profile.release], so its own release builds run at Cargo’s defaults (opt-level = 3, codegen-units = 16, lto = false, debug = false) except where a lane injects pins.
  • Even if it did, that would not reach you. cargo install rkat unpacks the published rkat crate as the root of its own temporary workspace; the repository’s root Cargo.toml is not part of that crate. A downstream binary that depends on meerkat = "=0.8.32" is likewise governed by its own root manifest, never by Meerkat’s.
So the consumer sets the profile. cargo install honors Cargo configuration from a .cargo/config.toml in the directory you run it from (or ~/.cargo/config.toml), from CARGO_PROFILE_RELEASE_* environment variables, and from --config on the command line. This was verified for this page with cargo install -v: with CARGO_PROFILE_RELEASE_OPT_LEVEL=1 set, rustc receives -C opt-level=1. Library embedders put the same tables in their own root Cargo.toml.

Low-memory profile

For hosts under 16 GiB, add the small-host pin. It is the only setting that moved meerkat-mob (~11.5 GiB at opt-level = 1, ~6.3 GiB at 0); it trades CPU on the orchestration path for memory, so measure your workload before shipping a binary built this way:
To keep everything else at full optimization and only pin the generated crates (the exact shape the release lane uses on Windows), drop the global opt-level line and keep:
The same settings on one command line, without a config file:
Per-package profile keys have no environment-variable form, which is why the package pin goes through --config; repeat the flag with profile.release.package.meerkat-mob.opt-level=0 for the small-host pin.

What each lever does

  • CARGO_BUILD_JOBS (or -j) caps the number of concurrent rustc processes. Aggregate memory is the sum of the compilers running at once, so this is the first lever on any memory-limited host. Wall time rises roughly in proportion.
  • opt-level: going from 3 to 1 removes most of LLVM’s expensive passes. Measured on the crates whose peak is in LLVM, it halved the rkat crate (~10.1 to ~4.8 GiB) and, together with the opt-level = 0 pin, meerkat-machine-schema (~9.4 to ~4.8 GiB), and it cut the whole build’s wall time from ~25 min 29 s to ~19 min 6 s. It did not move meerkat-mob; only opt-level = 0 did (~11.5 to ~6.3 GiB). The runtime cost of 1 is modest for an agent host that spends its time waiting on network I/O. opt-level = 0 is free for meerkat-machine-schema because it is a generated catalog, not hot code; on meerkat-mob it is a real trade.
  • codegen-units: LLVM optimizes a crate’s codegen units in parallel, bounded by your core count, so a crate split into 16 units can hold 16 LLVM contexts at once. Lowering it reduces that per-crate parallelism at the cost of a longer compile for that crate, and codegen-units = 1 also widens inlining scope, which can raise memory for the largest crates. Measure on your host before changing it; the release lane leaves it at 16.
  • lto: leave it at the default false. "thin" or "fat" LTO pulls the whole dependency graph into one optimization pass at link time and multiplies link-stage memory.
  • Features: meerkat-cli/Cargo.toml lists the subsystems behind rkat’s default features (mob, rpc-surface, openai-realtime, comms, schedule, workgraph, mcp, skills, storage backends, providers). A narrower --no-default-features --features ... build compiles fewer crates; dropping mob removes meerkat-mob, the crate that sets the memory floor above, along with meerkat-mob-mcp and meerkat-mob-pack. The repository’s surface feature matrix (scripts/run-surface-feature-matrix, behind make test-feature-matrix-surface) keeps three reduced rkat combinations compiling: --no-default-features --features session-store, session-store,mcp, and session-store,comms,mcp,workgraph. Other subsets are not exercised, so check that yours builds before relying on it. Reduced builds are source builds only; no reduced binaries are published (see the note under Homebrew Tap).

Containerizing The Binaries

A Rust binary built for a *-unknown-linux-gnu target links the builder’s glibc dynamically and records the newest glibc symbol version it used. The runtime image must provide a glibc at least that new, or the loader refuses to start the binary with version 'GLIBC_2.xx' not found. The reliable way to get that right in a two-stage image is to pin one Debian release for both stages, so the builder’s glibc is the runtime’s glibc:
Notes on that file:
  • ARG DEBIAN_RELEASE is declared before the first FROM and used in both FROM lines, so the two stages cannot drift apart when someone bumps one of them. rust:1.94-bookworm matches the pinned toolchain family.
  • --locked builds with the Cargo.lock shipped inside the rkat crate, so the container compiles the dependency set the release was tested with.
  • To build from a checkout instead of crates.io (unreleased commits, local patches), replace the RUN cargo install line with WORKDIR /src, COPY . ., and cargo build --locked -p rkat --release, and copy /src/target/release/rkat (the build stage above sets no WORKDIR, so without that line the checkout lands in /). The repository’s own examples/035-mdm-tux-rs/Dockerfile does exactly that for the example’s binaries, with the same rust:1.94-bookworm build stage and debian:bookworm-slim runtime stage.
  • The companion surfaces are separate crates with the same shape: cargo install meerkat-rpc (binary rkat-rpc), meerkat-rest (rkat-rest), and meerkat-mcp-server (rkat-mcp).
  • rkat resolves project realm state under .rkat/ in its working directory and the global realm’s config under ~/.rkat/ (see Configuration); mount a volume at /data, and point HOME at persisted storage if the global realm matters, so state outlives the container.
If you do not need to compile at all, the runtime stage alone can unpack a prebuilt Linux release archive. debian:bookworm-slim ships glibc 2.36, above the 2.31 floor the release binaries are built against (next section):
Verify the download against the release’s checksums.sha256 (and, if you use the GitHub CLI, its build provenance attestation; see below) before trusting it in production. Not covered by the prebuilt binaries: Alpine and other musl-based images. No musl target is published, and the glibc binaries are not tested under gcompat. Build from source on such a base or use a Debian-family image.

Where The Prebuilt Binaries Come From

.github/workflows/release.yml builds and publishes the release assets when a v* tag is pushed. Each of the four binaries (rkat, rkat-rpc, rkat-rest, rkat-mcp) is packaged per target as <binary>-<version>-<target>.tar.gz (.zip on Windows), alongside checksums.sha256 and index.json. The Homebrew tap and the SDK bootstrap consume these same assets. Targets:
  • x86_64-unknown-linux-gnu
  • aarch64-unknown-linux-gnu
  • aarch64-apple-darwin
  • x86_64-apple-darwin
  • x86_64-pc-windows-msvc
Every archive carries a GitHub build provenance attestation (actions/attest-build-provenance), which binds the file’s digest to the workflow run and commit that produced it:

Linux glibc floor

The Linux GNU binaries are built inside the buildpack-deps:bullseye image (Debian 11, glibc 2.31) on both release lanes: the BuildBuddy lane pins that image as the execution container in platforms/BUILD.bazel, and the GitHub-hosted lane runs its Linux jobs under the same container:, because building directly on the Ubuntu 24.04 runner (glibc 2.39) would silently raise the floor. Packaging then runs scripts/check-linux-release-binary-portability.sh on every Linux binary and fails the release if either check trips:
  1. Any versioned glibc symbol reference is newer than the declared floor (MEERKAT_GLIBC_FLOOR=2.31).
  2. The binary has a dynamic NEEDED entry on libssl or libcrypto. All first-party TLS is rustls, so an OpenSSL dependency means a crate regressed into a native-TLS stack.
The gate exists because v0.8.21 shipped binaries referencing GLIBC_2.34 that Debian 11 could not load; the floor is enforced on the produced file, not assumed from the build image. What that guarantees: a published Linux binary starts on any x86_64 or aarch64 distribution whose glibc is 2.31 or newer (Debian 11 and later, Ubuntu 20.04 and later, Debian-based container images from bullseye onward) and needs no OpenSSL runtime package. What it does not guarantee: musl-based systems (see above), or distributions older than glibc 2.31.

Other platforms

The macOS binaries carry no notarization and the release workflow asserts no minimum macOS version (there is no deployment-target pin or gate); the GitHub-hosted lane ad-hoc codesigns them. The Windows binary is x86_64 only. The packaging step checks that every binary embeds the release version, so rkat --version on an installed binary tells you exactly which release it is.

MobKit

MobKit’s gateway binaries are built and released by the meerkat-mobkit repository’s own lane; the glibc floor and portability gate described on this page do not apply to them. Start from the MobKit quickstart for its install and build paths.

See Also