---
title: "Load a prepared speech model offline in C#"
url: https://recursiveai.org/onespeech/cookbook/load-prepared-speech-model-without-network/
description: "Prepare a speech model once, then open its verified local files without network access. Preparation can download missing weights; the local loader does not."
version: "1.1.0"
---

# Load a prepared speech model offline in C#

**OneSpeech** is a free, open-source speech recognition library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneSpeech) · [GitHub](https://github.com/recursive-ai-oss/onespeech-csharp) · [Docs](https://recursiveai.org/onespeech/docs/) · [Download example (.zip)](https://recursiveai.org/onespeech/cookbook/load-prepared-speech-model-without-network/load-prepared-speech-model-without-network.zip)

Prepare model files once, then load them locally without downloads.

```shell
dotnet add package RecursiveAI.OneSpeech
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using OneSpeech.Catalog;
using OneSpeech.Moonshine;

// Prepare once with the full program before disconnecting.
var store = new ModelStore(source: new NoNetwork());
var resolved = await store.PullAsync("moonshine-ai/moonshine-tiny");
using var recognizer = MoonshineRecognizer.Load(resolved);
Console.WriteLine("Loaded prepared model.");

sealed class NoNetwork : ModelSource
{
    public override ValueTask<Stream> OpenReadAsync(
        CatalogEntry model, CatalogFile file, CancellationToken cancellationToken)
        => throw new IOException("Model is not prepared: " + file.Name);
}
```

Preparation downloads about 0.11 GB of Moonshine tiny publisher files on first use and caches them and locally converted graphs in `~/.cache/onespeech/models` on Linux ([change the cache location](https://recursiveai.org/onespeech/cookbook/configure-speech-model-cache/)); the offline snippet refuses downloads.

```text
Loaded prepared model.
```

Unzip [the example](https://recursiveai.org/onespeech/cookbook/load-prepared-speech-model-without-network/load-prepared-speech-model-without-network.zip), then `dotnet run`.

## Full program

The zip includes `sample.flac`, the unmodified mono 16 kHz [LibriSpeech test-clean utterance 1089-134686-0001](https://www.openslr.org/12): “Stuff it into you, his belly counseled him.” Attribution: Vassil Panayotov, Guoguo Chen, Daniel Povey and Sanjeev Khudanpur (2015), [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Recognition output can differ from that reference.

The full program prepares once unless `--offline` is passed, then resolves with missing-file requests refused before calling `Load`. Keep the prepared model cache for the offline run.

Preparation downloads about 0.11 GB of Moonshine tiny publisher files on first use and caches them and locally converted graphs in `~/.cache/onespeech/models` on Linux ([change the cache location](https://recursiveai.org/onespeech/cookbook/configure-speech-model-cache/)); the offline snippet refuses downloads.

```csharp
using OneSpeech;
using OneSpeech.Audio;
using OneSpeech.Catalog;
using OneSpeech.Moonshine;
using OneSpeech.Vad;
using OneSpeech.Pipeline;

if (!args.Contains("--offline"))
    await ModelStore.Default.PullAsync("moonshine-ai/moonshine-tiny");
var store = new ModelStore(source: new NoNetwork());
var resolved = await store.PullAsync("moonshine-ai/moonshine-tiny");
// Preparation ends here. Load opens resolved local files without network access.
using var recognizer = MoonshineRecognizer.Load(resolved);
using var vad = new SileroVad();
var pipeline = new VadTranscriptionPipeline(recognizer, vad);
Console.WriteLine(pipeline.Transcribe("sample.flac", "en").Text);

sealed class NoNetwork : ModelSource
{
    public override ValueTask<Stream> OpenReadAsync(
        CatalogEntry model, CatalogFile file, CancellationToken cancellationToken)
        => throw new IOException("Model is not prepared: " + file.Name);
}
```

Output:

```text
Duff it into you, his belly counseled him.
```

When a required source file is missing in offline mode, `NoNetwork` throws `IOException` instead of downloading it.

## Variations

### Skip preparation when offline

After the first successful run, reuse the same cache and run:

```shell
dotnet run -- --offline
```

```text
Duff it into you, his belly counseled him.
```

## Related recipes

- [Transcribe an audio file](https://recursiveai.org/onespeech/cookbook/transcribe-audio-file/)
- [Run offline speech recognition](https://recursiveai.org/onespeech/cookbook/offline-speech-recognition/)
- [Get word timestamps](https://recursiveai.org/onespeech/cookbook/word-timestamps/)

## The short answer

Use `ModelStore.PullAsync` during preparation to obtain a `ResolvedModel`, then call `MoonshineRecognizer.Load(resolved)` to open those files. The canonical `Load(ResolvedModel, ...)` path does not use the network. The full program additionally supplies a `ModelSource` that refuses missing files, so an offline run fails instead of fetching a replacement.

## How do I load a local speech model without a network in C#?

Separate model acquisition from model loading. `LoadAsync` is convenient when an application is allowed to pull missing publisher files, verify them and convert them locally. Use `Load(resolved)`, not `LoadAsync`, when you must not download. The synchronous canonical loader takes the directory and provenance together in a `ResolvedModel`.

The snippet at the top resolves an already-prepared model through a source that refuses missing-file downloads, then opens it locally. Run the full program once while connected before using that snippet offline. The full program makes the boundary explicit: it prepares with the default store, resolves again through a store backed by `NoNetwork`, and loads that result. The second store can read prepared files, but its custom source throws if any required source file is absent. Its successful result is not a guessed directory or a manually reconstructed cache filename.

### Run after disconnecting

Run the example once with access to the publisher, then keep the prepared cache. On later runs, use the `--offline` variation to skip preparation. Restore the .NET project before disconnecting too: NuGet package acquisition is a separate step from the model loader. The zip includes source and sample audio, not the model weights or an installed .NET runtime.

The sample’s offline run used the prepared Moonshine tiny model and printed the actual recognition text shown above: the model mishears “Stuff” as “Duff.” The custom source was configured to refuse all missing-file requests. This demonstrates the library’s model-fetch boundary; it does not claim that arbitrary code elsewhere in your application cannot open a network connection.

### Preserve verified files and provenance

`ModelStore` verifies present files on the first open for each new store. Local conversion is part of preparation, not permission to substitute an arbitrary graph for a catalog checkpoint. Copying a single weight file into a directory with a plausible name is not equivalent to keeping a prepared model. Preserve the publisher and runtime files needed by that catalog entry, and obtain the resolved directory/provenance pair through the store.

If you use a nondefault cache location, configure both preparation and offline resolution to use it. An empty cache is not a fallback request in the offline example: it becomes an I/O failure from `NoNetwork`. A corrupt cached file is reported through the integrity contract rather than silently accepted as an offline model. 

### Load from your own folder

Your own cache folder is supported: pass its path to `ModelStore` during both preparation and offline resolution, as shown in the [cache directory recipe](https://recursiveai.org/onespeech/cookbook/configure-speech-model-cache/). Defaults are `~/.cache/onespeech/models` on Linux (or `$XDG_CACHE_HOME/onespeech/models` when set), `~/Library/Caches/OneSpeech/models` on macOS, and `LocalApplicationData/OneSpeech/models` on Windows. The pinned loading docs also retain string-directory overloads for manually converted or custom graphs; those require the documented family graph format, not arbitrary downloaded weights. Use the store’s `ResolvedModel` for normal catalog checkpoints.

### Model access rules still apply

The example uses an ungated English checkpoint. A local cache does not imply access rights for every model family. Gated catalog entries still require explicit authorization, and the store checks credentials even for cached gated files. Do not use the prepared-cache workflow to bypass publisher access conditions or redistribute restricted model graphs.

For the canonical loader pair and ownership rules, see [loading and composing roles](https://recursiveai.org/onespeech/docs/loading/). The [offline speech recognition recipe](https://recursiveai.org/onespeech/cookbook/offline-speech-recognition/) covers a different deployment choice: restoring packaged weights beside the application rather than resolving an already-prepared catalog cache.
