OpenMOSS logo

MOSS-TTS

MOSS-TTSvfamilyCurrent
byOpenMOSSOpenMOSS(open source)
Released February 7, 2026
Context--
CategorySpeech Generation Model

About MOSS-TTS

MOSS-TTS Family is an open-source speech and sound generation model family from MOSI.AI and the OpenMOSS team. It covers long-form speech, multi-speaker dialogue, voice and character design, environmental sound effects, and streaming TTS.

Capabilities

text to speechaudio generationvoice designstreaming tts

Input Modalities

textaudio

Output Modalities

audio

Technical Details

API Identifier
https://github.com/OpenMOSS/MOSS-TTS
Category
Speech Generation Model

Tags

open-sourcegithubai-modelresearchmoss-tts

Run this model yourself

Local and cloud setup for MOSS-TTS, with exact configurations, sources and clear evidence limits.

Find a model for your hardware
Local hardwareDocumented setupSources checked Oct 16, 2026

Synthesize short speech with native MOSS-TTS Nano on a CPU desktop

Use the official MOSS-TTS family’s CPU-oriented Nano member and its complete native ONNX audio codec. One fixed original English sentence is spoken using a reader-recorded own or authorized voice reference. All 16 audio codebooks are retained; the configured 512 global-row budget separates text, reference frames and generated audio frames. Output is 48 kHz stereo PCM WAV through a private educational endpoint. Fit, speed, voice similarity and speech quality remain unmeasured.

Hardware
Intel Core i5-12400 desktop, 4 inference threads, 32 GiB RAM
Runtime
Native MOSS-TTS Nano complete ONNX CPU speech pipeline native Nano source 8b7bcc9341b3b4ef3a3a58ba1338a7d85ff133eb; ONNX Runtime1.23.2 CPU; SentencePiece0.2.1; NumPy2.2.3; Python3.12 · CPU
Model format
ONNX external-data bundle · Publisher native ONNX float32 tensors; CPUExecutionProvider, all 16 audio codebooks retained · Publisher native ONNX float32 export; all 16 codebooks, no additional quantization
Workload
512 token context · 1 concurrent

Based on source documentation; this setup has not been executed by OpenTools. Memory: unknown. No compatible measurement available.

Read the complete setup guide

Before you start

  • Use an owned Intel Core i5-12400 Ubuntu 24.04 x86_64 desktop with 32 GiB RAM, 40 GiB free disk and four inference threads. These allocations are not measured fit or minima.
  • Frozen MOSS-TTS is a family record dated February 2026. Its official parent README expressly recommends the later April 2026 Nano member for CPU use. This guide names that exact member; it does not silently replace flagship 8B, local-transformer, realtime, sound-effect or other family modes. Family guide total remains open.
  • Review exact native code/model/codec Apache 2.0 terms and use only a reader-owned or authorized voice recording. Record 1–5seconds of clean natural speech, not a sine wave or a copied third-party example. Export uncompressed PCM16 WAV at 48 kHz mono or stereo. The native Nano request uses reference audio codes and target text without a separate reference-transcript field.
  • No recording or voice is supplied or transmitted by the author. Place your recording as own-recording.wav in the dedicated workspace. On cloud, transfer it privately using existing SSH/scp yourself; never expose the speech service publicly. Treat recordings/generated voice as private data.
  • Use Python 3.12 with source-qualified ONNX Runtime 1.23.2 CPU, NumPy 2.2.3 and SentencePiece 0.2.1. Direct official OrtCpuRuntime module is selected; upperCLI imports Torch/torchaudio and is not invoked. No dependency stripping or source patch is needed for this native low-level API. This is documented, unexecuted compatibility.

Exact model and requirements

Artifact:
OpenMOSS Team / MOSI.AI / MOSS-TTS Nano100M ONNX with complete Nano ONNX audio tokenizer · checkpoint OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX
Publisher revision:
f52645cb467506d8e18e746ddd59482685b74e58
Artifact digest:
The complete TTS+codec multi-file bundle has no single publisher aggregate digest. All 18 selected files have exact per-file publisher LFS SHA256 or Git blob SHA1 in the reader manifest.
Download size:
0.71 GiB
Memory requirement (unknown):
Unknown — No measured full-process peak for native TTS/global-local state/codec/ONNX sessions at this budget.
System RAM / disk:
Unknown / Unknown
Placement:
single device. One CPU host; one reference, one fixed sentence and one serial request. All native ONNX sessions use CPUExecutionProvider; no CUDA, disk or GPU offload.
KV cache:
Native ONNX global/local past/present state is retained as exported. No separate cache-precision measurement or alternative cache quantization is claimed.
Workload:
512 token context; Unknown; Not applicable; concurrency 1; batch 1; cache cold; repetitions Unknown.
Tokenizer:
Pinned Nano SentencePiece text tokenizer plus16-codebook audio rows, native prompt templates · f52645cb467506d8e18e746ddd59482685b74e58
Sampling / reasoning:
Source-supported greedy mode: do_sample=False, sample_mode=greedy, max_new_frames 125 and native audio repetition penalty 1.2. Target fixed English sentence is encoded raw like native encode_text, <=75 text tokens. Reference1–5sec 48 kHz stereo PCM16 is encoded with full native codec; <=64 reference audio frames admitted. Native template plus reference/text prefill and 125 reserved future global rows must fit 512. No RNG-quality claim or native text normalization feature is implied. Native global input is a sequence of 17-wide rows: one text/slot field plus16 audio codebooks. A reference or generated audio frame occupies one global row, not 16 positions. Codec downsample 3840 at 48 kHz is 12.5 frames/sec; 125 output frames cap 10 sec before end effects. Generated frames must be1–124, rejecting potentially truncated cap-length speech. Local decoder/codebook steps are distinct. The original PyTorch config 32768 is not claimed as a proven ONNX maximum; 512 is this author-selected checked budget.
Runtime prerequisites:
Ubuntu 24.04 x86_64; See prerequisites above.

Official artifact · License

Actual native Nano code LICENSE is Apache License 2.0; parent family README expressly licenses family models under Apache 2.0, and both exact official ONNX TTS/codec cards declare Apache 2.0. Retain applicable license/copyright/NOTICE and change/patent obligations. This does not grant rights to imitate any person: use your own or otherwise authorized recording. No built-in voice identity, dataset/media right or downstream suitability clearance is inferred. No credentials, terms acceptance or recording acquisition is performed by this guide author.

  • 762,195,739 bytes sums official ONNX graph/external-data file sizes, not complete installed footprint or runtime RAM.
  • 32 GiB RAM, 40 GiB disk and four threads are planning choices. Native session initialization, graph optimization, K/V state, codec and OS add unknown peak overhead. No publisher throughput or sibling fit is transferred.

One-time setup and hardware

hardware
Unknown — Existing desktop purchase price and incremental hardware cost depend on the reader; unknown.
Existing desktop purchase price and incremental hardware cost depend on the reader; unknown. Tax: unknown.
setup
Unknown — Initial provisioning/build time and labor cost are unknown.
Initial provisioning/build time and labor cost are unknown. Tax: unknown.

Recurring costs

electricity
Unknown — Actual wall power, electricity rate and powered hours are unknown; no TDP-to-wall-power conversion.
Actual wall power, electricity rate and powered hours are unknown; no TDP-to-wall-power conversion. Tax: unknown.
storage
Unknown — Incremental cost of local disk capacity is unknown. The 40 GiB free-disk walkthrough allocation is an author choice, not a measured requirement.
Incremental cost of local disk capacity is unknown. The 40 GiB free-disk walkthrough allocation is an author choice, not a measured requirement. Tax: unknown.
egress
Unknown — Data transfer price, allowances and traffic volume are unknown.
Data transfer price, allowances and traffic volume are unknown. Tax: unknown.
administration
Unknown — Maintenance labor rate and hours are unknown.
Maintenance labor rate and hours are unknown. Tax: unknown.

Install the runtime

  1. 1. Create the native CPU runtime environment

    Install source-qualified CPU packages and check resolution. Read the official low-level runtime directly from its pinned checkout; do not install the broader web/PyTorch project just to run this self-contained NumPy/ORT module. The complete TTS and codec sessions remain unchanged.

    Command
    set -e
    sudo apt-get update
    sudo apt-get install -y python3.12 python3.12-venv git curl ca-certificates openssl
    mkdir -p "$HOME/moss-nano-speech/models"
    cd "$HOME/moss-nano-speech"
    python3.12 -m venv .venv
    .venv/bin/python -m pip install 'onnxruntime==1.23.2' 'numpy==2.2.3' 'sentencepiece==0.2.1' 'huggingface-hub==0.36.2'
    git clone https://github.com/OpenMOSS/MOSS-TTS-Nano.git native-moss-nano
    git -C native-moss-nano checkout --detach 8b7bcc9341b3b4ef3a3a58ba1338a7d85ff133eb
    sha256sum --check <<'SHA256'
    99477efaff28c12c0c8e16a6b4aab4e880a70851693fa303d932415ba180cbd5  native-moss-nano/ort_cpu_runtime.py
    SHA256
    .venv/bin/python -m pip check
    .venv/bin/python -m pip freeze > resolved-environment.txt

    Expected result: Pinned native module and packages resolve, source hash/pip check pass and resolution is saved. No native session, weights or inference has run.

Download the model

  1. 1. Write the full TTS and codec manifest

    Select all 18 exact official TTS/codec files, including every external-data graph dependency and tokenizer. LFS SHA256 and regular Git blob SHA1 are distinguished; Git SHA1 is identity rather than modern adversarial integrity. No mutable default auto-download is used.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    cat > component-manifest.json <<'MANIFEST'
    {
      "files": [
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "README.md",
          "bytes": 6095,
          "algorithm": "git-blob-sha1",
          "digest": "7b61e0071d117e56943eefc753b7b65208da02fe"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "browser_poc_manifest.json",
          "bytes": 503354,
          "algorithm": "git-blob-sha1",
          "digest": "8a04b980c3b9ea2f56747650ea255efe421ada38"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_decode_step.onnx",
          "bytes": 291483,
          "algorithm": "sha256",
          "digest": "698cbc2fc1c2feca16e5895614ed52bbb32ded10f236c076f477b2e69abf32d8"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_global_shared.data",
          "bytes": 440813568,
          "algorithm": "sha256",
          "digest": "bce8312c3df6a44545302cae229b61054fe0672e0b252ba59cba47adeed831dc"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_local_cached_step.onnx",
          "bytes": 53685,
          "algorithm": "sha256",
          "digest": "aa9035fefc1c138a951a8bcfc0374fb03a25f1ece67f7f7f53bce349b84a1dd5"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_local_decoder.onnx",
          "bytes": 49231,
          "algorithm": "sha256",
          "digest": "51aa754301b38550a5f9adda0ad93bd3dc95819afb511e6dcabf4a90b345a454"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_local_fixed_sampled_frame.onnx",
          "bytes": 471262,
          "algorithm": "sha256",
          "digest": "40cdb00efc171c450cf91468e01429caa41b0252222cd308e978f58fe354afa8"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_local_shared.data",
          "bytes": 229678080,
          "algorithm": "sha256",
          "digest": "bae7782032c0fb12490ab42afe009f87ae6c75a0f0596fc7b5c08e4d5ee93916"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_prefill.onnx",
          "bytes": 283305,
          "algorithm": "sha256",
          "digest": "d56126dcd0574c2f15d98fc6b35eda68d0386b5bd9c5e38e28548d6f2ea8f3db"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "tokenizer.model",
          "bytes": 470897,
          "algorithm": "sha256",
          "digest": "c353ee1479b536bf414c1b247f5542b6607fb8ae91320e5af1781fee200fddff"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "tts_browser_onnx_meta.json",
          "bytes": 4487,
          "algorithm": "git-blob-sha1",
          "digest": "883597607ce139b2c4871468396af2c088ed2fe0"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "README.md",
          "bytes": 4892,
          "algorithm": "git-blob-sha1",
          "digest": "46f3d445b16e8a283dd62fc9bdeec8aa35a59337"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "codec_browser_onnx_meta.json",
          "bytes": 17036,
          "algorithm": "git-blob-sha1",
          "digest": "886953a56489516b847b7c1c953bde063eb78faa"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "moss_audio_tokenizer_decode_full.onnx",
          "bytes": 681902,
          "algorithm": "sha256",
          "digest": "0fbbafe3fd4afa2a019af5c5ced204af6e2d1db044fa40f021525d2aee95b4ac"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "moss_audio_tokenizer_decode_shared.data",
          "bytes": 44198912,
          "algorithm": "sha256",
          "digest": "e69d52e0f4e84ca27850557ee54face46632d3a5a16c89bd246c7c408466dcad"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "moss_audio_tokenizer_decode_step.onnx",
          "bytes": 351400,
          "algorithm": "sha256",
          "digest": "9527c86a29e1837edec1f74db57d5eeaadb3a715af3382703566460afed25855"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "moss_audio_tokenizer_encode.data",
          "bytes": 44507136,
          "algorithm": "sha256",
          "digest": "aa751265b2bab2887eac224484546b194875aa7494b607115439b3dc6b228a2c"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "moss_audio_tokenizer_encode.onnx",
          "bytes": 815775,
          "algorithm": "sha256",
          "digest": "eadea4a645abdcf98714c7aead122ee2ce7da6e080f9f80b977cd1ca8e19473a"
        }
      ]
    }
    MANIFEST
    sha256sum --check <<'SHA256'
    7b0f9b6a346d2f1d384c1a2fd8d9dc3215246392c720b7f64cace88540eef19f  component-manifest.json
    SHA256

    Expected result: Exact full manifest verifies before reader weight retrieval.

  2. 2. Retrieve and verify every native component

    Reader-only official downloads. Preserve the native sibling-directory layout referenced by the manifest. Reject any file size/hash mismatch before creating ONNX sessions; codec encode/full-decode/stream-decode and all native TTS graphs remain present even when a branch is unused.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    .venv/bin/python - <<'PYDOWNLOAD'
    import hashlib,json,pathlib,shutil
    from huggingface_hub import hf_hub_download
    for row in json.load(open('component-manifest.json'))['files']:
     src=hf_hub_download(row['repository'],row['file'],revision=row['revision'])
     dst=pathlib.Path('models')/row['directory']/row['file'];dst.parent.mkdir(parents=True,exist_ok=True);shutil.copyfile(src,dst)
     assert dst.stat().st_size==row['bytes']
     h=hashlib.sha256() if row['algorithm']=='sha256' else hashlib.sha1()
     if row['algorithm']=='git-blob-sha1':h.update(('blob '+str(row['bytes'])+'\0').encode())
     with dst.open('rb') as f:
      for b in iter(lambda:f.read(1024*1024),b''):h.update(b)
     assert h.hexdigest()==row['digest'],row['file']
    print('All18 components verified, no native session loaded.')
    PYDOWNLOAD

    Expected result: All 18 pinned files verify. Download cache duplication adds disk overhead;40 GiB is not a measured minimum.

Configure

  1. 1. Prepare your own authorized reference recording

    Copy only your own/authorized clean 1–5sec 48 kHz PCM16 recording to own-recording.wav first. This step validates its format and duplicates mono channels like the native wrapper, with no resampling or fabricated voice. Listen to the original to confirm intelligibility/authorization; matching reference transcript is not a native Nano input. Existing original file remains intact.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    .venv/bin/python - <<'PYREFERENCE'
    import pathlib,wave
    p=pathlib.Path('own-recording.wav');assert p.stat().st_size<=1000000
    with wave.open(str(p),'rb') as src:
     c=src.getnchannels();n=src.getnframes()
     assert c in (1,2) and src.getsampwidth()==2 and src.getframerate()==48000 and src.getcomptype()=='NONE'
     assert 48000<=n<=240000
     b=src.readframes(n);assert len(b)==n*c*2 and any(b)
    if c==1:b=b''.join(b[i:i+2]*2 for i in range(0,len(b),2))
    with wave.open('reference.wav','wb') as dst:
     dst.setnchannels(2);dst.setsampwidth(2);dst.setframerate(48000);dst.writeframes(b)
    PYREFERENCE
    chmod 600 own-recording.wav reference.wav
    sha256sum reference.wav > reference.sha256

    Expected result: Actual reader-owned reference.wav is 48 kHz stereo PCM16,1–5seconds. Hash is reader-generated, not a fabricated publisher recording digest.

  2. 2. Create a stable private service key

    Create only when absent and keep it private. Existing key is preserved; empty/invalid key must be restored rather than silently rotated.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    umask 077
    if [ ! -e .api-key ]; then openssl rand -hex 32 > .api-key; fi
    chmod 600 .api-key
    test -s .api-key || { echo "Restore empty key before continuing"; exit 1; }

    Expected result: Private64-character lowercase hexadecimal key preserved or created.

  3. 3. Create the native speech harness

    Reader-authored harness invokes unchanged official OrtCpuRuntime with complete codec. It verifies components/reference/source, encodes full 16-codebook reference, checks text/native global rows and rejects empty or cap-length generated speech. No arbitrary upload, file, URL, text or voice request is accepted.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    cat > moss_speech.py <<'PYSERVER'
    """Reader-authored educational fixed-input MOSS-TTS Nano native ONNX harness; not publisher server."""
    import hashlib
    import hmac
    import json
    import math
    import pathlib
    from http.server import BaseHTTPRequestHandler, HTTPServer
    
    ROOT = pathlib.Path(__file__).resolve().parent
    PORT = 8996
    
    
    TEXT = 'Welcome to this private speech demonstration.'
    
    
    def validate_audio(result):
        import base64
        import io
        import wave
        wav, rows, frames, reference_hash, clipped = result
        if type(rows) is not int or type(frames) is not int or not 0 < frames < 125 or not 0 < rows <= 387:
            raise ValueError('native_row_budget')
        if not isinstance(wav, bytes) or not 44 <= len(wav) <= 1920044:
            raise ValueError('wav_size')
        with wave.open(io.BytesIO(wav), 'rb') as audio:
            if (audio.getnchannels(),audio.getsampwidth(),audio.getframerate(),audio.getcomptype()) != (2,2,48000,'NONE'):
                raise ValueError('wav_format')
            samples = audio.getnframes()
            if not 0 < samples <= frames*3840:
                raise ValueError('wav_duration')
            if len(audio.readframes(samples)) != samples*4:
                raise ValueError('wav_truncated')
        if not isinstance(reference_hash,str) or len(reference_hash)!=64 or any(c not in '0123456789abcdef' for c in reference_hash):
            raise ValueError('reference_hash')
        if type(clipped) is not int or not 0 <= clipped <= samples*2:
            raise ValueError('clipping_count')
        return {'fixture':'reader-own-reference-v1','text':TEXT,'mime_type':'audio/wav',
                'wav_base64':base64.b64encode(wav).decode('ascii'),'sample_rate':48000,
                'channels':2,'sample_frames':samples,'generated_audio_frames':frames,
                'audio_codebook_count':16,'prefill_global_rows':rows,
                'configured_global_row_budget':512,'max_new_audio_frames':125,
                'reference_sha256':reference_hash,'pcm_clipped_samples':clipped,
                'generated_text_tokens':None}
    
    
    def make_encoder():
        # Reader execution only. Fake boundary tests do not import NumPy/ORT/SentencePiece.
        import sys
        import io
        import wave
        import numpy as np
        import sentencepiece as spm
        manifest=json.loads((ROOT/'component-manifest.json').read_text())
        for row in manifest['files']:
            file=ROOT/'models'/row['directory']/row['file']
            if file.stat().st_size!=row['bytes']:raise RuntimeError('component_size')
            h=hashlib.sha256() if row['algorithm']=='sha256' else hashlib.sha1()
            if row['algorithm']=='git-blob-sha1':h.update(('blob '+str(row['bytes'])+'\0').encode())
            with file.open('rb') as stream:
                for chunk in iter(lambda:stream.read(1024*1024),b''):h.update(chunk)
            if h.hexdigest()!=row['digest']:raise RuntimeError('component_hash')
        reference=ROOT/'reference.wav'
        if reference.stat().st_size>1000000:raise RuntimeError('reference_size')
        reference_hash=hashlib.sha256(reference.read_bytes()).hexdigest()
        if reference_hash!=(ROOT/'reference.sha256').read_text().split()[0]:raise RuntimeError('reference_changed')
        with wave.open(str(reference),'rb') as audio:
            if (audio.getnchannels(),audio.getsampwidth(),audio.getframerate(),audio.getcomptype())!=(2,2,48000,'NONE'):
                raise RuntimeError('reference_format')
            n=audio.getnframes()
            if not 48000<=n<=240000:raise RuntimeError('reference_duration')
            pcm=audio.readframes(n)
            if len(pcm)!=n*4:raise RuntimeError('reference_truncated')
        waveform=np.frombuffer(pcm,dtype='<i2').astype(np.float32).reshape(-1,2).T[None,:,:]/32768.0
        if not np.any(waveform):raise RuntimeError('reference_silent')
        native=ROOT/'native-moss-nano'
        if hashlib.sha256((native/'ort_cpu_runtime.py').read_bytes()).hexdigest()!='99477efaff28c12c0c8e16a6b4aab4e880a70851693fa303d932415ba180cbd5':
            raise RuntimeError('native_source_hash')
        sys.path.insert(0,str(native))
        from ort_cpu_runtime import OrtCpuRuntime
        runtime=OrtCpuRuntime(ROOT/'models',thread_count=4,max_new_frames=125,
                              do_sample=False,sample_mode='greedy',execution_provider='cpu')
        if runtime.manifest['tts_config']['n_vq']!=16:raise RuntimeError('codebook_count')
        config=runtime.codec_meta['codec_config']
        if (config['sample_rate'],config['channels'],config['downsample_rate'],config['num_quantizers'])!=(48000,2,3840,16):
            raise RuntimeError('codec_variant')
        outputs=runtime.sessions['codec_encode'].run(None,{'waveform':waveform,
                          'input_lengths':np.asarray([n],dtype=np.int32)})
        named=dict(zip([o.name for o in runtime.sessions['codec_encode'].get_outputs()],outputs,strict=True))
        codes=np.asarray(named['audio_codes']);length=int(np.asarray(named['audio_code_lengths']).reshape(-1)[0])
        if not np.issubdtype(codes.dtype,np.integer) or not 0<length<=64 or codes.ndim!=3 or codes.shape[0]!=1 or codes.shape[1]<length or codes.shape[2]!=16:
            raise RuntimeError('reference_code_shape')
        prompt_codes=codes[0,:length,:].astype(np.int32).tolist()
        if any(not 0<=v<1024 for row in prompt_codes for v in row):raise RuntimeError('reference_code_range')
        tokenizer=spm.SentencePieceProcessor(model_file=str(ROOT/'models/MOSS-TTS-Nano-100M-ONNX/tokenizer.model'))
        text_ids=[int(x) for x in tokenizer.encode(TEXT,out_type=int)]
        if not 0<len(text_ids)<=75:raise RuntimeError('text_budget')
        request=runtime.build_voice_clone_request_rows(prompt_codes,text_ids)
        rows=len(request['inputIds'])
        if rows+125>512 or any(len(row)!=17 for row in request['inputIds']):raise RuntimeError('global_row_budget')
    
        def encode():
            frames=runtime.generate_audio_frames(request)
            if not 0<len(frames)<125:raise RuntimeError('empty_or_capped_audio')
            if any(len(row)!=16 or any(type(v) is not int or not 0<=v<1024 for v in row) for row in frames):
                raise RuntimeError('generated_code_shape')
            channels,count=runtime.decode_full_audio(frames)
            if len(channels)!=2 or not 0<count<=len(frames)*3840:raise RuntimeError('decoded_audio_shape')
            data=np.stack(channels,axis=1)
            if data.shape!=(count,2) or not np.isfinite(data).all() or not np.any(data):raise RuntimeError('decoded_audio_finiteness')
            clipped=int(np.count_nonzero(np.abs(data)>1))
            pcm_out=(np.clip(data,-1,1)*32767).round().astype('<i2').tobytes()
            buffer=io.BytesIO()
            with wave.open(buffer,'wb') as out:
                out.setnchannels(2);out.setsampwidth(2);out.setframerate(48000);out.writeframes(pcm_out)
            return buffer.getvalue(),rows,len(frames),reference_hash,clipped
        return encode
    
    
    PYSERVER

    Expected result: First complete loader/codec/native-row/PCM validation fragment written.

  4. 4. Complete and verify private speech transport

    Append the bounded private loopback protocol. Syntax/hash checks do not load any native model.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    cat >> moss_speech.py <<'PYSERVER'
    def create_server(encoder, key, port=PORT):
        if not isinstance(key, str) or len(key) != 64 or any(c not in '0123456789abcdef' for c in key):
            raise ValueError('key_format')
    
        class Handler(BaseHTTPRequestHandler):
            def setup(self):
                super().setup()
                self.connection.settimeout(5)
    
            def log_message(self, *_):
                pass  # Do not log Bearer keys or request bodies.
    
            def reply(self, status, obj):
                data = json.dumps(obj, allow_nan=False, separators=(',', ':')).encode()
                self.send_response(status)
                self.send_header('Content-Type', 'application/json')
                self.send_header('Content-Length', str(len(data)))
                self.send_header('Connection', 'close')
                self.end_headers()
                self.wfile.write(data)
                self.close_connection = True
    
            def authorized(self):
                values = self.headers.get_all('Authorization', [])
                return len(values) == 1 and hmac.compare_digest(values[0].encode(), ('Bearer ' + key).encode())
    
            def do_GET(self):
                if not self.authorized():
                    return self.reply(401, {'error': 'unauthorized'})
                if self.path != '/health':
                    return self.reply(404, {'error': 'not_found'})
                return self.reply(200, {'ready': True, 'configured_global_row_budget': 512, 'audio_codebook_count': 16,
                                        'generated_text_tokens': None})
    
            def do_POST(self):
                if not self.authorized():
                    return self.reply(401, {'error': 'unauthorized'})
                if self.path != '/v1/fixed-speech':
                    return self.reply(404, {'error': 'not_found'})
                if self.headers.get('Transfer-Encoding') is not None:
                    return self.reply(400, {'error': 'transfer_encoding_unsupported'})
                lengths = self.headers.get_all('Content-Length', [])
                if len(lengths) != 1:
                    return self.reply(411, {'error': 'content_length_required'})
                raw = lengths[0]
                if not raw or len(raw) > 4 or any(c not in '0123456789' for c in raw):
                    return self.reply(413, {'error': 'invalid_or_excessive_length'})
                n = int(raw)
                if n < 1 or n > 256:
                    return self.reply(413, {'error': 'body_limit'})
                if self.headers.get_content_type() != 'application/json':
                    return self.reply(415, {'error': 'json_required'})
                try:
                    body = self.rfile.read(n)
                except (TimeoutError, OSError):
                    return self.reply(408, {'error': 'body_timeout'})
                if len(body) != n:
                    return self.reply(400, {'error': 'incomplete_body'})
                try:
                    def unique(pairs):
                        obj = {}
                        for k, v in pairs:
                            if k in obj:
                                raise ValueError('duplicate_key')
                            obj[k] = v
                        return obj
                    request = json.loads(body.decode('utf-8'), object_pairs_hook=unique,
                                         parse_constant=lambda _: (_ for _ in ()).throw(ValueError('constant')))
                except (ValueError, UnicodeError, RecursionError):
                    return self.reply(400, {'error': 'invalid_json'})
                if type(request) is not dict or request != {'fixture': 'reader-own-reference-v1'}:
                    return self.reply(400, {'error': 'fixed_fixture_required'})
                try:
                    result = validate_audio(encoder())
                except Exception:
                    return self.reply(500, {'error': 'native_speech_or_wav_failure'})
                return self.reply(200, result)
    
        return HTTPServer(('127.0.0.1', port), Handler)
    
    
    if __name__ == '__main__':
        secret = (ROOT / '.api-key').read_text().strip()
        if len(secret)!=64 or any(c not in '0123456789abcdef' for c in secret):
            raise ValueError('key_format')
        encoder = make_encoder()
        with create_server(encoder, secret) as server:
            server.serve_forever()
    PYSERVER
    .venv/bin/python -m py_compile moss_speech.py
    sha256sum --check <<'SHA256'
    af684e3b9446099087a52b34b47000c41e532b68c2132e520714b2de984f4c90  moss_speech.py
    SHA256

    Expected result: Exact complete wrapper bytes and syntax pass without native imports.

Start the model

  1. 1. Start the private native CPU speech experiment

    All native sessions use CPUExecutionProvider with four intra-op threads and one inter-op thread. Reference codec encoding occurs at startup; each generation builds native global/local cache afresh. No warmup, browser/web app, optional text-normalization path or streaming optimization is claimed.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    .venv/bin/python moss_speech.py

    Expected result: After actual reader-side verified native load/reference encoding, loopback 127.0.0.1:8996 serves. Stop on load/codec/budget errors; successful speech is not claimed by the author.

Connect to your endpoint

http://127.0.0.1:8996/v1/fixed-speech

Same trusted desktop loopback only.

Access: loopback. Private Bearer key for health and synthesis. Send a JSON object with fixture set to reader-own-reference-v1. Response contains base64 WAV, reference hash, global rows, audio frames, codebook count and clipping count. No arbitrary text/audio/uploads/URLs/files or transcript fields are accepted.

Verify the setup

  1. 1. Validate WAV structure and listen to the speech

    From a second server terminal, request only the fixed original sentence. Check exact native metadata, cap rejection and valid stereo PCM WAV; then listen locally. Speech must say the displayed sentence intelligibly, not merely have valid bytes. Voice similarity, prosody, pronunciation, loudness and clipping need human inspection; no quality metric is fabricated. Missing key must return 401.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    MOSS_KEY="$(cat .api-key)"
    curl --fail http://127.0.0.1:8996/health -H "Authorization: Bearer $MOSS_KEY"
    curl --fail http://127.0.0.1:8996/v1/fixed-speech -H "Authorization: Bearer $MOSS_KEY" -H 'Content-Type: application/json' -d '{"fixture":"reader-own-reference-v1"}' -o speech.json
    curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8996/health
    unset MOSS_KEY
    .venv/bin/python - <<'PYVERIFY'
    import base64,io,json,wave
    d=json.load(open('speech.json'));assert d['generated_text_tokens'] is None
    assert d['configured_global_row_budget']==512 and d['audio_codebook_count']==16
    assert 0<d['generated_audio_frames']<125 and d['prefill_global_rows']+125<=512
    b=base64.b64decode(d['wav_base64'],validate=True);assert len(b)<=1920044
    with wave.open(io.BytesIO(b),'rb') as w:
     assert (w.getnchannels(),w.getsampwidth(),w.getframerate(),w.getcomptype())==(2,2,48000,'NONE')
     assert w.getnframes()==d['sample_frames'] and 0<w.getnframes()<=d['generated_audio_frames']*3840
     assert len(w.readframes(w.getnframes()))==w.getnframes()*4
    open('speech.wav','wb').write(b)
    print(d['text']);print('Listen to speech.wav; validate intelligibility, voice permission and clipping manually.')
    PYVERIFY

    Expected result: Authenticated response has valid bounded WAV/native metadata; missing key 401. Listen to speech.wav with your local player. Invalid/capped/silent/nonfinite outputs fail; valid structure is not intelligibility or voice-quality proof.

Performance evidence

No compatible measurement available. Documented support and estimated memory do not establish response speed.

Limits and troubleshooting

  • Frozen family date is February 2026; Nano is an explicitly named April 2026 member recommended by the official parent for CPU use. Flagship 8B, 1.7B/4B local transformer, realtime, dialogue, sound effects, voice design, other backends/streaming and other family jobs remain open. This pair is not family-complete.
  • 512 is this author-selected total native global-row budget: template/reference/text prefill plus 125 reserved future rows. Each audio row contains 16 codebook IDs plus a text/slot column. It is not 512 text tokens, a scalar-codec-ID count or a proven ONNX global maximum.
  • Native codec 48 kHz stereo/downsample 3840 gives 12.5 audio frames/sec. 125 generated frames cap 10 sec; reaching the cap is rejected as potentially truncated. Decoded sample count/end effects are checked rather than equated blindly with frame count.
  • Full native codec encode/full-decode/stream-decode sessions and all required TTS graphs/external data remain pinned. Using the low-level native NumPy/ORT API is not an incomplete text-only substitute. The broader CLI imports Torch/torchaudio and is not falsely represented as a no-Torch entrypoint.
  • Original target text is fixed English. Reader must provide own/authorized clean speech; no provided identity or recording is copied. Native Nano uses reference codes and target text without a separate reference-transcript field. Robust normalization, long-text chunking and arbitrary scripts are outside this bounded raw-token path.
  • Greedy mode is a source-supported choice differing from default fixed sampling. Native repetition penalty 1.2 remains. No measured naturalness, speaker similarity, sample-rate quality, realtime factor or benchmark transfer is claimed.
  • Native decoded arrays must be finite/nonzero. PCM16 conversion clips values outside[-1,1] and reports clipped sample count; listening and loudness assessment remain necessary. No generated WAV, actual reference, listening result or successful native inference is supplied here.
  • 32 GiB RAM, 40 GiB disk and four threads are selected allocations; full session/cache/codec load fit and performance remain unknown. Official file sizes are not runtime memory or a measured minimum.
  • Reader-authored serial HTTPServer uses private key/loopback, bounded strict fixed JSON and WAV/output checks, but no inference deadline or public multiuser-production guarantee. Reference/voice files remain private reader data.
  • Fake WAV tests validate protocol/security/output boundaries only, not native ONNX compatibility, codec correctness, speech content or voice consent. Native code/model/source pins remain unchanged.
Native component/provider/session error
Stop on hash, metadata, ONNX load or provider errors. Use exact 18-file layout/native module and CPUExecutionProvider; do not float to main, omit codec graphs or replace the full speech path with a text-only endpoint.
Reference format, code shape or budget error
Use your own/authorized 1–5sec 48 kHz PCM16 mono/stereo source and exact reference conversion/hash. Listen to clean natural speech first. Do not substitute synthetic tones or silently crop/reduce codebooks; shorter authorized recording may be needed to fit this chosen row bound.
401 or rejected request
Use matching private 64-character key and exact fixed selector. Extra text, audio, URLs, files and transcript fields are rejected.
Capped, silent, nonfinite or unintelligible speech
Treat cap-length/empty/nonfinite as failure and inspect actual audio/reference. A valid WAV can be wrong or sound poor; record the defect. This guide promises no native quality or speed. Do not remove completion guards or use someone else’s voice to force a demo.

Stop the model

  1. 1. Stop the private speech service

    Press Ctrl+C in the foreground terminal and unset client key. Confirm loopback speech endpoint no longer serves.

    Command
    unset MOSS_KEY

    Expected result: Native sessions and endpoint stop; reader reference/output files remain until removed.

Uninstall

  1. 1. Remove the dedicated private experiment

    Preserve desired authorized outputs, source/license and audit records first. Confirm directory before deletion. Remove only these selected model caches if unused by other work; shared Ubuntu packages remain. Deleting files is not a promise of forensic erasure.

    Command
    rm -r -- "$HOME/moss-nano-speech"
    rm -r -- "$HOME/.cache/huggingface/hub/models--OpenMOSS-Team--MOSS-TTS-Nano-100M-ONNX"
    rm -r -- "$HOME/.cache/huggingface/hub/models--OpenMOSS-Team--MOSS-Audio-Tokenizer-Nano-ONNX"

    Expected result: The dedicated native checkout, virtual environment, model bundle, reference recording, outputs, private key and selected model caches are removed if present.

Sources and verification

Sources checked Oct 16, 2026. Verification source

Compare this configuration
Cloud serverDocumented setupSources checked Oct 16, 2026

Synthesize short speech with native MOSS-TTS Nano on a private CPU VM

Use the official MOSS-TTS family’s CPU-oriented Nano member and its complete native ONNX audio codec. One fixed original English sentence is spoken using a reader-recorded own or authorized voice reference. All 16 audio codebooks are retained; the configured 512 global-row budget separates text, reference frames and generated audio frames. Output is 48 kHz stereo PCM WAV through a private educational endpoint. Fit, speed, voice similarity and speech quality remain unmeasured.

Hardware
AWS r7i.xlarge, 32 GiB RAM, Ubuntu x86_64
Runtime
Native MOSS-TTS Nano complete ONNX CPU speech pipeline native Nano source 8b7bcc9341b3b4ef3a3a58ba1338a7d85ff133eb; ONNX Runtime1.23.2 CPU; SentencePiece0.2.1; NumPy2.2.3; Python3.12 · CPU
Model format
ONNX external-data bundle · Publisher native ONNX float32 tensors; CPUExecutionProvider, all 16 audio codebooks retained · Publisher native ONNX float32 export; all 16 codebooks, no additional quantization
Workload
512 token context · 1 concurrent

Based on source documentation; this setup has not been executed by OpenTools. Memory: unknown. No compatible measurement available.

Read the complete setup guide

Before you start

  • Use an owned AWS r7i.xlarge Ubuntu 24.04 x86_64 VM in us-east-1 with 32 GiB RAM, four vCPUs and chosen 40 GiB gp3 volume. Restrict existing SSH access; keep speech port 8996 closed. Provisioning/billing are reader actions.
  • Frozen MOSS-TTS is a family record dated February 2026. Its official parent README expressly recommends the later April 2026 Nano member for CPU use. This guide names that exact member; it does not silently replace flagship 8B, local-transformer, realtime, sound-effect or other family modes. Family guide total remains open.
  • Review exact native code/model/codec Apache 2.0 terms and use only a reader-owned or authorized voice recording. Record 1–5seconds of clean natural speech, not a sine wave or a copied third-party example. Export uncompressed PCM16 WAV at 48 kHz mono or stereo. The native Nano request uses reference audio codes and target text without a separate reference-transcript field.
  • No recording or voice is supplied or transmitted by the author. Place your recording as own-recording.wav in the dedicated workspace. On cloud, transfer it privately using existing SSH/scp yourself; never expose the speech service publicly. Treat recordings/generated voice as private data.
  • Use Python 3.12 with source-qualified ONNX Runtime 1.23.2 CPU, NumPy 2.2.3 and SentencePiece 0.2.1. Direct official OrtCpuRuntime module is selected; upperCLI imports Torch/torchaudio and is not invoked. No dependency stripping or source patch is needed for this native low-level API. This is documented, unexecuted compatibility.

Exact model and requirements

Artifact:
OpenMOSS Team / MOSI.AI / MOSS-TTS Nano100M ONNX with complete Nano ONNX audio tokenizer · checkpoint OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX
Publisher revision:
f52645cb467506d8e18e746ddd59482685b74e58
Artifact digest:
The complete TTS+codec multi-file bundle has no single publisher aggregate digest. All 18 selected files have exact per-file publisher LFS SHA256 or Git blob SHA1 in the reader manifest.
Download size:
0.71 GiB
Memory requirement (unknown):
Unknown — No measured full-process peak for native TTS/global-local state/codec/ONNX sessions at this budget.
System RAM / disk:
Unknown / Unknown
Placement:
single device. One CPU host; one reference, one fixed sentence and one serial request. All native ONNX sessions use CPUExecutionProvider; no CUDA, disk or GPU offload.
KV cache:
Native ONNX global/local past/present state is retained as exported. No separate cache-precision measurement or alternative cache quantization is claimed.
Workload:
512 token context; Unknown; Not applicable; concurrency 1; batch 1; cache cold; repetitions Unknown.
Tokenizer:
Pinned Nano SentencePiece text tokenizer plus16-codebook audio rows, native prompt templates · f52645cb467506d8e18e746ddd59482685b74e58
Sampling / reasoning:
Source-supported greedy mode: do_sample=False, sample_mode=greedy, max_new_frames 125 and native audio repetition penalty 1.2. Target fixed English sentence is encoded raw like native encode_text, <=75 text tokens. Reference1–5sec 48 kHz stereo PCM16 is encoded with full native codec; <=64 reference audio frames admitted. Native template plus reference/text prefill and 125 reserved future global rows must fit 512. No RNG-quality claim or native text normalization feature is implied. Native global input is a sequence of 17-wide rows: one text/slot field plus16 audio codebooks. A reference or generated audio frame occupies one global row, not 16 positions. Codec downsample 3840 at 48 kHz is 12.5 frames/sec; 125 output frames cap 10 sec before end effects. Generated frames must be1–124, rejecting potentially truncated cap-length speech. Local decoder/codebook steps are distinct. The original PyTorch config 32768 is not claimed as a proven ONNX maximum; 512 is this author-selected checked budget.
Runtime prerequisites:
Ubuntu 24.04 x86_64; See prerequisites above.

Official artifact · License

Actual native Nano code LICENSE is Apache License 2.0; parent family README expressly licenses family models under Apache 2.0, and both exact official ONNX TTS/codec cards declare Apache 2.0. Retain applicable license/copyright/NOTICE and change/patent obligations. This does not grant rights to imitate any person: use your own or otherwise authorized recording. No built-in voice identity, dataset/media right or downstream suitability clearance is inferred. No credentials, terms acceptance or recording acquisition is performed by this guide author.

  • 762,195,739 bytes sums official ONNX graph/external-data file sizes, not complete installed footprint or runtime RAM.
  • 32 GiB RAM, 40 GiB disk and four threads are planning choices. Native session initialization, graph optimization, K/V state, codec and OS add unknown peak overhead. No publisher throughput or sibling fit is transferred.

One-time setup and hardware

hardware
Not applicable — AWS hardware acquisition is included in compute rental; no purchased GPU required. Any separate hardware cost is not applicable.
AWS hardware acquisition is included in compute rental; no purchased GPU required. Any separate hardware cost is not applicable. Tax: unknown.
setup
Unknown — Initial provisioning/build time and labor cost are unknown.
Initial provisioning/build time and labor cost are unknown. Tax: unknown.

Recurring costs

compute
$0.26 / hour
AWS public us-east-1 Linux On-Demand rate USD 0.2646 per billed instance-hour, no paid preinstalled software. Eight hours USD 2.1168; 24 hours USD 6.3504 compute. Idle running hours bill. Storage, egress, tax and administration separate; no Spot/reservation/discount assumed. Capacity/quota unverified. Region: us-east-1. SKU: r7i.xlarge Linux On-Demand. Tax: excluded. Price dated Oct 15, 2026. Price source
storage
$0.08 / GB month
US East (N. Virginia) gp3 USD 0.08 per GB-month; AWS defines GB as 1024^3 bytes. Selected 40 GiB root volume is USD 3.20 per full month, prorated by provisioned duration. Baseline 3000 IOPS / 125 MB/s; no extras assumed. Retained snapshots/resources separate; storage persists after instance stop. Tax excluded. Region: us-east-1. SKU: EBS gp3 baseline 3000 IOPS / 125 MB/s. Tax: excluded. Price dated Oct 16, 2026. Price source
egress
Unknown — Data transfer price, allowances and traffic volume are unknown.
Data transfer price, allowances and traffic volume are unknown. Tax: unknown.
administration
Unknown — Maintenance labor rate and hours are unknown.
Maintenance labor rate and hours are unknown. Tax: unknown.

Install the runtime

  1. 1. Create the native CPU runtime environment

    Install source-qualified CPU packages and check resolution. Read the official low-level runtime directly from its pinned checkout; do not install the broader web/PyTorch project just to run this self-contained NumPy/ORT module. The complete TTS and codec sessions remain unchanged.

    Command
    set -e
    sudo apt-get update
    sudo apt-get install -y python3.12 python3.12-venv git curl ca-certificates openssl
    mkdir -p "$HOME/moss-nano-speech/models"
    cd "$HOME/moss-nano-speech"
    python3.12 -m venv .venv
    .venv/bin/python -m pip install 'onnxruntime==1.23.2' 'numpy==2.2.3' 'sentencepiece==0.2.1' 'huggingface-hub==0.36.2'
    git clone https://github.com/OpenMOSS/MOSS-TTS-Nano.git native-moss-nano
    git -C native-moss-nano checkout --detach 8b7bcc9341b3b4ef3a3a58ba1338a7d85ff133eb
    sha256sum --check <<'SHA256'
    99477efaff28c12c0c8e16a6b4aab4e880a70851693fa303d932415ba180cbd5  native-moss-nano/ort_cpu_runtime.py
    SHA256
    .venv/bin/python -m pip check
    .venv/bin/python -m pip freeze > resolved-environment.txt

    Expected result: Pinned native module and packages resolve, source hash/pip check pass and resolution is saved. No native session, weights or inference has run.

Download the model

  1. 1. Write the full TTS and codec manifest

    Select all 18 exact official TTS/codec files, including every external-data graph dependency and tokenizer. LFS SHA256 and regular Git blob SHA1 are distinguished; Git SHA1 is identity rather than modern adversarial integrity. No mutable default auto-download is used.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    cat > component-manifest.json <<'MANIFEST'
    {
      "files": [
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "README.md",
          "bytes": 6095,
          "algorithm": "git-blob-sha1",
          "digest": "7b61e0071d117e56943eefc753b7b65208da02fe"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "browser_poc_manifest.json",
          "bytes": 503354,
          "algorithm": "git-blob-sha1",
          "digest": "8a04b980c3b9ea2f56747650ea255efe421ada38"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_decode_step.onnx",
          "bytes": 291483,
          "algorithm": "sha256",
          "digest": "698cbc2fc1c2feca16e5895614ed52bbb32ded10f236c076f477b2e69abf32d8"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_global_shared.data",
          "bytes": 440813568,
          "algorithm": "sha256",
          "digest": "bce8312c3df6a44545302cae229b61054fe0672e0b252ba59cba47adeed831dc"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_local_cached_step.onnx",
          "bytes": 53685,
          "algorithm": "sha256",
          "digest": "aa9035fefc1c138a951a8bcfc0374fb03a25f1ece67f7f7f53bce349b84a1dd5"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_local_decoder.onnx",
          "bytes": 49231,
          "algorithm": "sha256",
          "digest": "51aa754301b38550a5f9adda0ad93bd3dc95819afb511e6dcabf4a90b345a454"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_local_fixed_sampled_frame.onnx",
          "bytes": 471262,
          "algorithm": "sha256",
          "digest": "40cdb00efc171c450cf91468e01429caa41b0252222cd308e978f58fe354afa8"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_local_shared.data",
          "bytes": 229678080,
          "algorithm": "sha256",
          "digest": "bae7782032c0fb12490ab42afe009f87ae6c75a0f0596fc7b5c08e4d5ee93916"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "moss_tts_prefill.onnx",
          "bytes": 283305,
          "algorithm": "sha256",
          "digest": "d56126dcd0574c2f15d98fc6b35eda68d0386b5bd9c5e38e28548d6f2ea8f3db"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "tokenizer.model",
          "bytes": 470897,
          "algorithm": "sha256",
          "digest": "c353ee1479b536bf414c1b247f5542b6607fb8ae91320e5af1781fee200fddff"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-TTS-Nano-100M-ONNX",
          "revision": "f52645cb467506d8e18e746ddd59482685b74e58",
          "directory": "MOSS-TTS-Nano-100M-ONNX",
          "file": "tts_browser_onnx_meta.json",
          "bytes": 4487,
          "algorithm": "git-blob-sha1",
          "digest": "883597607ce139b2c4871468396af2c088ed2fe0"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "README.md",
          "bytes": 4892,
          "algorithm": "git-blob-sha1",
          "digest": "46f3d445b16e8a283dd62fc9bdeec8aa35a59337"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "codec_browser_onnx_meta.json",
          "bytes": 17036,
          "algorithm": "git-blob-sha1",
          "digest": "886953a56489516b847b7c1c953bde063eb78faa"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "moss_audio_tokenizer_decode_full.onnx",
          "bytes": 681902,
          "algorithm": "sha256",
          "digest": "0fbbafe3fd4afa2a019af5c5ced204af6e2d1db044fa40f021525d2aee95b4ac"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "moss_audio_tokenizer_decode_shared.data",
          "bytes": 44198912,
          "algorithm": "sha256",
          "digest": "e69d52e0f4e84ca27850557ee54face46632d3a5a16c89bd246c7c408466dcad"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "moss_audio_tokenizer_decode_step.onnx",
          "bytes": 351400,
          "algorithm": "sha256",
          "digest": "9527c86a29e1837edec1f74db57d5eeaadb3a715af3382703566460afed25855"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "moss_audio_tokenizer_encode.data",
          "bytes": 44507136,
          "algorithm": "sha256",
          "digest": "aa751265b2bab2887eac224484546b194875aa7494b607115439b3dc6b228a2c"
        },
        {
          "repository": "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano-ONNX",
          "revision": "ceff0d0749bfb3fa2d61149794ec6feef0d1e1ae",
          "directory": "MOSS-Audio-Tokenizer-Nano-ONNX",
          "file": "moss_audio_tokenizer_encode.onnx",
          "bytes": 815775,
          "algorithm": "sha256",
          "digest": "eadea4a645abdcf98714c7aead122ee2ce7da6e080f9f80b977cd1ca8e19473a"
        }
      ]
    }
    MANIFEST
    sha256sum --check <<'SHA256'
    7b0f9b6a346d2f1d384c1a2fd8d9dc3215246392c720b7f64cace88540eef19f  component-manifest.json
    SHA256

    Expected result: Exact full manifest verifies before reader weight retrieval.

  2. 2. Retrieve and verify every native component

    Reader-only official downloads. Preserve the native sibling-directory layout referenced by the manifest. Reject any file size/hash mismatch before creating ONNX sessions; codec encode/full-decode/stream-decode and all native TTS graphs remain present even when a branch is unused.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    .venv/bin/python - <<'PYDOWNLOAD'
    import hashlib,json,pathlib,shutil
    from huggingface_hub import hf_hub_download
    for row in json.load(open('component-manifest.json'))['files']:
     src=hf_hub_download(row['repository'],row['file'],revision=row['revision'])
     dst=pathlib.Path('models')/row['directory']/row['file'];dst.parent.mkdir(parents=True,exist_ok=True);shutil.copyfile(src,dst)
     assert dst.stat().st_size==row['bytes']
     h=hashlib.sha256() if row['algorithm']=='sha256' else hashlib.sha1()
     if row['algorithm']=='git-blob-sha1':h.update(('blob '+str(row['bytes'])+'\0').encode())
     with dst.open('rb') as f:
      for b in iter(lambda:f.read(1024*1024),b''):h.update(b)
     assert h.hexdigest()==row['digest'],row['file']
    print('All18 components verified, no native session loaded.')
    PYDOWNLOAD

    Expected result: All 18 pinned files verify. Download cache duplication adds disk overhead;40 GiB is not a measured minimum.

Configure

  1. 1. Prepare your own authorized reference recording

    Copy only your own/authorized clean 1–5sec 48 kHz PCM16 recording to own-recording.wav first. This step validates its format and duplicates mono channels like the native wrapper, with no resampling or fabricated voice. Listen to the original to confirm intelligibility/authorization; matching reference transcript is not a native Nano input. Existing original file remains intact.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    .venv/bin/python - <<'PYREFERENCE'
    import pathlib,wave
    p=pathlib.Path('own-recording.wav');assert p.stat().st_size<=1000000
    with wave.open(str(p),'rb') as src:
     c=src.getnchannels();n=src.getnframes()
     assert c in (1,2) and src.getsampwidth()==2 and src.getframerate()==48000 and src.getcomptype()=='NONE'
     assert 48000<=n<=240000
     b=src.readframes(n);assert len(b)==n*c*2 and any(b)
    if c==1:b=b''.join(b[i:i+2]*2 for i in range(0,len(b),2))
    with wave.open('reference.wav','wb') as dst:
     dst.setnchannels(2);dst.setsampwidth(2);dst.setframerate(48000);dst.writeframes(b)
    PYREFERENCE
    chmod 600 own-recording.wav reference.wav
    sha256sum reference.wav > reference.sha256

    Expected result: Actual reader-owned reference.wav is 48 kHz stereo PCM16,1–5seconds. Hash is reader-generated, not a fabricated publisher recording digest.

  2. 2. Create a stable private service key

    Create only when absent and keep it private. Existing key is preserved; empty/invalid key must be restored rather than silently rotated.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    umask 077
    if [ ! -e .api-key ]; then openssl rand -hex 32 > .api-key; fi
    chmod 600 .api-key
    test -s .api-key || { echo "Restore empty key before continuing"; exit 1; }

    Expected result: Private64-character lowercase hexadecimal key preserved or created.

  3. 3. Create the native speech harness

    Reader-authored harness invokes unchanged official OrtCpuRuntime with complete codec. It verifies components/reference/source, encodes full 16-codebook reference, checks text/native global rows and rejects empty or cap-length generated speech. No arbitrary upload, file, URL, text or voice request is accepted.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    cat > moss_speech.py <<'PYSERVER'
    """Reader-authored educational fixed-input MOSS-TTS Nano native ONNX harness; not publisher server."""
    import hashlib
    import hmac
    import json
    import math
    import pathlib
    from http.server import BaseHTTPRequestHandler, HTTPServer
    
    ROOT = pathlib.Path(__file__).resolve().parent
    PORT = 8996
    
    
    TEXT = 'Welcome to this private speech demonstration.'
    
    
    def validate_audio(result):
        import base64
        import io
        import wave
        wav, rows, frames, reference_hash, clipped = result
        if type(rows) is not int or type(frames) is not int or not 0 < frames < 125 or not 0 < rows <= 387:
            raise ValueError('native_row_budget')
        if not isinstance(wav, bytes) or not 44 <= len(wav) <= 1920044:
            raise ValueError('wav_size')
        with wave.open(io.BytesIO(wav), 'rb') as audio:
            if (audio.getnchannels(),audio.getsampwidth(),audio.getframerate(),audio.getcomptype()) != (2,2,48000,'NONE'):
                raise ValueError('wav_format')
            samples = audio.getnframes()
            if not 0 < samples <= frames*3840:
                raise ValueError('wav_duration')
            if len(audio.readframes(samples)) != samples*4:
                raise ValueError('wav_truncated')
        if not isinstance(reference_hash,str) or len(reference_hash)!=64 or any(c not in '0123456789abcdef' for c in reference_hash):
            raise ValueError('reference_hash')
        if type(clipped) is not int or not 0 <= clipped <= samples*2:
            raise ValueError('clipping_count')
        return {'fixture':'reader-own-reference-v1','text':TEXT,'mime_type':'audio/wav',
                'wav_base64':base64.b64encode(wav).decode('ascii'),'sample_rate':48000,
                'channels':2,'sample_frames':samples,'generated_audio_frames':frames,
                'audio_codebook_count':16,'prefill_global_rows':rows,
                'configured_global_row_budget':512,'max_new_audio_frames':125,
                'reference_sha256':reference_hash,'pcm_clipped_samples':clipped,
                'generated_text_tokens':None}
    
    
    def make_encoder():
        # Reader execution only. Fake boundary tests do not import NumPy/ORT/SentencePiece.
        import sys
        import io
        import wave
        import numpy as np
        import sentencepiece as spm
        manifest=json.loads((ROOT/'component-manifest.json').read_text())
        for row in manifest['files']:
            file=ROOT/'models'/row['directory']/row['file']
            if file.stat().st_size!=row['bytes']:raise RuntimeError('component_size')
            h=hashlib.sha256() if row['algorithm']=='sha256' else hashlib.sha1()
            if row['algorithm']=='git-blob-sha1':h.update(('blob '+str(row['bytes'])+'\0').encode())
            with file.open('rb') as stream:
                for chunk in iter(lambda:stream.read(1024*1024),b''):h.update(chunk)
            if h.hexdigest()!=row['digest']:raise RuntimeError('component_hash')
        reference=ROOT/'reference.wav'
        if reference.stat().st_size>1000000:raise RuntimeError('reference_size')
        reference_hash=hashlib.sha256(reference.read_bytes()).hexdigest()
        if reference_hash!=(ROOT/'reference.sha256').read_text().split()[0]:raise RuntimeError('reference_changed')
        with wave.open(str(reference),'rb') as audio:
            if (audio.getnchannels(),audio.getsampwidth(),audio.getframerate(),audio.getcomptype())!=(2,2,48000,'NONE'):
                raise RuntimeError('reference_format')
            n=audio.getnframes()
            if not 48000<=n<=240000:raise RuntimeError('reference_duration')
            pcm=audio.readframes(n)
            if len(pcm)!=n*4:raise RuntimeError('reference_truncated')
        waveform=np.frombuffer(pcm,dtype='<i2').astype(np.float32).reshape(-1,2).T[None,:,:]/32768.0
        if not np.any(waveform):raise RuntimeError('reference_silent')
        native=ROOT/'native-moss-nano'
        if hashlib.sha256((native/'ort_cpu_runtime.py').read_bytes()).hexdigest()!='99477efaff28c12c0c8e16a6b4aab4e880a70851693fa303d932415ba180cbd5':
            raise RuntimeError('native_source_hash')
        sys.path.insert(0,str(native))
        from ort_cpu_runtime import OrtCpuRuntime
        runtime=OrtCpuRuntime(ROOT/'models',thread_count=4,max_new_frames=125,
                              do_sample=False,sample_mode='greedy',execution_provider='cpu')
        if runtime.manifest['tts_config']['n_vq']!=16:raise RuntimeError('codebook_count')
        config=runtime.codec_meta['codec_config']
        if (config['sample_rate'],config['channels'],config['downsample_rate'],config['num_quantizers'])!=(48000,2,3840,16):
            raise RuntimeError('codec_variant')
        outputs=runtime.sessions['codec_encode'].run(None,{'waveform':waveform,
                          'input_lengths':np.asarray([n],dtype=np.int32)})
        named=dict(zip([o.name for o in runtime.sessions['codec_encode'].get_outputs()],outputs,strict=True))
        codes=np.asarray(named['audio_codes']);length=int(np.asarray(named['audio_code_lengths']).reshape(-1)[0])
        if not np.issubdtype(codes.dtype,np.integer) or not 0<length<=64 or codes.ndim!=3 or codes.shape[0]!=1 or codes.shape[1]<length or codes.shape[2]!=16:
            raise RuntimeError('reference_code_shape')
        prompt_codes=codes[0,:length,:].astype(np.int32).tolist()
        if any(not 0<=v<1024 for row in prompt_codes for v in row):raise RuntimeError('reference_code_range')
        tokenizer=spm.SentencePieceProcessor(model_file=str(ROOT/'models/MOSS-TTS-Nano-100M-ONNX/tokenizer.model'))
        text_ids=[int(x) for x in tokenizer.encode(TEXT,out_type=int)]
        if not 0<len(text_ids)<=75:raise RuntimeError('text_budget')
        request=runtime.build_voice_clone_request_rows(prompt_codes,text_ids)
        rows=len(request['inputIds'])
        if rows+125>512 or any(len(row)!=17 for row in request['inputIds']):raise RuntimeError('global_row_budget')
    
        def encode():
            frames=runtime.generate_audio_frames(request)
            if not 0<len(frames)<125:raise RuntimeError('empty_or_capped_audio')
            if any(len(row)!=16 or any(type(v) is not int or not 0<=v<1024 for v in row) for row in frames):
                raise RuntimeError('generated_code_shape')
            channels,count=runtime.decode_full_audio(frames)
            if len(channels)!=2 or not 0<count<=len(frames)*3840:raise RuntimeError('decoded_audio_shape')
            data=np.stack(channels,axis=1)
            if data.shape!=(count,2) or not np.isfinite(data).all() or not np.any(data):raise RuntimeError('decoded_audio_finiteness')
            clipped=int(np.count_nonzero(np.abs(data)>1))
            pcm_out=(np.clip(data,-1,1)*32767).round().astype('<i2').tobytes()
            buffer=io.BytesIO()
            with wave.open(buffer,'wb') as out:
                out.setnchannels(2);out.setsampwidth(2);out.setframerate(48000);out.writeframes(pcm_out)
            return buffer.getvalue(),rows,len(frames),reference_hash,clipped
        return encode
    
    
    PYSERVER

    Expected result: First complete loader/codec/native-row/PCM validation fragment written.

  4. 4. Complete and verify private speech transport

    Append the bounded private loopback protocol. Syntax/hash checks do not load any native model.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    cat >> moss_speech.py <<'PYSERVER'
    def create_server(encoder, key, port=PORT):
        if not isinstance(key, str) or len(key) != 64 or any(c not in '0123456789abcdef' for c in key):
            raise ValueError('key_format')
    
        class Handler(BaseHTTPRequestHandler):
            def setup(self):
                super().setup()
                self.connection.settimeout(5)
    
            def log_message(self, *_):
                pass  # Do not log Bearer keys or request bodies.
    
            def reply(self, status, obj):
                data = json.dumps(obj, allow_nan=False, separators=(',', ':')).encode()
                self.send_response(status)
                self.send_header('Content-Type', 'application/json')
                self.send_header('Content-Length', str(len(data)))
                self.send_header('Connection', 'close')
                self.end_headers()
                self.wfile.write(data)
                self.close_connection = True
    
            def authorized(self):
                values = self.headers.get_all('Authorization', [])
                return len(values) == 1 and hmac.compare_digest(values[0].encode(), ('Bearer ' + key).encode())
    
            def do_GET(self):
                if not self.authorized():
                    return self.reply(401, {'error': 'unauthorized'})
                if self.path != '/health':
                    return self.reply(404, {'error': 'not_found'})
                return self.reply(200, {'ready': True, 'configured_global_row_budget': 512, 'audio_codebook_count': 16,
                                        'generated_text_tokens': None})
    
            def do_POST(self):
                if not self.authorized():
                    return self.reply(401, {'error': 'unauthorized'})
                if self.path != '/v1/fixed-speech':
                    return self.reply(404, {'error': 'not_found'})
                if self.headers.get('Transfer-Encoding') is not None:
                    return self.reply(400, {'error': 'transfer_encoding_unsupported'})
                lengths = self.headers.get_all('Content-Length', [])
                if len(lengths) != 1:
                    return self.reply(411, {'error': 'content_length_required'})
                raw = lengths[0]
                if not raw or len(raw) > 4 or any(c not in '0123456789' for c in raw):
                    return self.reply(413, {'error': 'invalid_or_excessive_length'})
                n = int(raw)
                if n < 1 or n > 256:
                    return self.reply(413, {'error': 'body_limit'})
                if self.headers.get_content_type() != 'application/json':
                    return self.reply(415, {'error': 'json_required'})
                try:
                    body = self.rfile.read(n)
                except (TimeoutError, OSError):
                    return self.reply(408, {'error': 'body_timeout'})
                if len(body) != n:
                    return self.reply(400, {'error': 'incomplete_body'})
                try:
                    def unique(pairs):
                        obj = {}
                        for k, v in pairs:
                            if k in obj:
                                raise ValueError('duplicate_key')
                            obj[k] = v
                        return obj
                    request = json.loads(body.decode('utf-8'), object_pairs_hook=unique,
                                         parse_constant=lambda _: (_ for _ in ()).throw(ValueError('constant')))
                except (ValueError, UnicodeError, RecursionError):
                    return self.reply(400, {'error': 'invalid_json'})
                if type(request) is not dict or request != {'fixture': 'reader-own-reference-v1'}:
                    return self.reply(400, {'error': 'fixed_fixture_required'})
                try:
                    result = validate_audio(encoder())
                except Exception:
                    return self.reply(500, {'error': 'native_speech_or_wav_failure'})
                return self.reply(200, result)
    
        return HTTPServer(('127.0.0.1', port), Handler)
    
    
    if __name__ == '__main__':
        secret = (ROOT / '.api-key').read_text().strip()
        if len(secret)!=64 or any(c not in '0123456789abcdef' for c in secret):
            raise ValueError('key_format')
        encoder = make_encoder()
        with create_server(encoder, secret) as server:
            server.serve_forever()
    PYSERVER
    .venv/bin/python -m py_compile moss_speech.py
    sha256sum --check <<'SHA256'
    af684e3b9446099087a52b34b47000c41e532b68c2132e520714b2de984f4c90  moss_speech.py
    SHA256

    Expected result: Exact complete wrapper bytes and syntax pass without native imports.

Start the model

  1. 1. Start the private native CPU speech experiment

    All native sessions use CPUExecutionProvider with four intra-op threads and one inter-op thread. Reference codec encoding occurs at startup; each generation builds native global/local cache afresh. No warmup, browser/web app, optional text-normalization path or streaming optimization is claimed.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    .venv/bin/python moss_speech.py

    Expected result: After actual reader-side verified native load/reference encoding, loopback 127.0.0.1:8996 serves. Stop on load/codec/budget errors; successful speech is not claimed by the author.

Connect to your endpoint

http://127.0.0.1:8996/v1/fixed-speech

VM loopback only. Existing SSH tunnel may use ssh -N -L 8996:127.0.0.1:8996 USER@VM_IP. Keep EC2 port 8996 closed. Shown checks run on server; transfer your WAV privately yourself.

Access: loopback. Private Bearer key for health and synthesis. Send a JSON object with fixture set to reader-own-reference-v1. Response contains base64 WAV, reference hash, global rows, audio frames, codebook count and clipping count. No arbitrary text/audio/uploads/URLs/files or transcript fields are accepted.

Verify the setup

  1. 1. Validate WAV structure and listen to the speech

    From a second server terminal, request only the fixed original sentence. Check exact native metadata, cap rejection and valid stereo PCM WAV; then listen locally. Speech must say the displayed sentence intelligibly, not merely have valid bytes. Voice similarity, prosody, pronunciation, loudness and clipping need human inspection; no quality metric is fabricated. Missing key must return 401.

    Command
    set -e
    cd "$HOME/moss-nano-speech"
    MOSS_KEY="$(cat .api-key)"
    curl --fail http://127.0.0.1:8996/health -H "Authorization: Bearer $MOSS_KEY"
    curl --fail http://127.0.0.1:8996/v1/fixed-speech -H "Authorization: Bearer $MOSS_KEY" -H 'Content-Type: application/json' -d '{"fixture":"reader-own-reference-v1"}' -o speech.json
    curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8996/health
    unset MOSS_KEY
    .venv/bin/python - <<'PYVERIFY'
    import base64,io,json,wave
    d=json.load(open('speech.json'));assert d['generated_text_tokens'] is None
    assert d['configured_global_row_budget']==512 and d['audio_codebook_count']==16
    assert 0<d['generated_audio_frames']<125 and d['prefill_global_rows']+125<=512
    b=base64.b64decode(d['wav_base64'],validate=True);assert len(b)<=1920044
    with wave.open(io.BytesIO(b),'rb') as w:
     assert (w.getnchannels(),w.getsampwidth(),w.getframerate(),w.getcomptype())==(2,2,48000,'NONE')
     assert w.getnframes()==d['sample_frames'] and 0<w.getnframes()<=d['generated_audio_frames']*3840
     assert len(w.readframes(w.getnframes()))==w.getnframes()*4
    open('speech.wav','wb').write(b)
    print(d['text']);print('Listen to speech.wav; validate intelligibility, voice permission and clipping manually.')
    PYVERIFY

    Expected result: Authenticated response has valid bounded WAV/native metadata; missing key 401. Listen to speech.wav with your local player. Invalid/capped/silent/nonfinite outputs fail; valid structure is not intelligibility or voice-quality proof.

Performance evidence

No compatible measurement available. Documented support and estimated memory do not establish response speed.

Limits and troubleshooting

  • Frozen family date is February 2026; Nano is an explicitly named April 2026 member recommended by the official parent for CPU use. Flagship 8B, 1.7B/4B local transformer, realtime, dialogue, sound effects, voice design, other backends/streaming and other family jobs remain open. This pair is not family-complete.
  • 512 is this author-selected total native global-row budget: template/reference/text prefill plus 125 reserved future rows. Each audio row contains 16 codebook IDs plus a text/slot column. It is not 512 text tokens, a scalar-codec-ID count or a proven ONNX global maximum.
  • Native codec 48 kHz stereo/downsample 3840 gives 12.5 audio frames/sec. 125 generated frames cap 10 sec; reaching the cap is rejected as potentially truncated. Decoded sample count/end effects are checked rather than equated blindly with frame count.
  • Full native codec encode/full-decode/stream-decode sessions and all required TTS graphs/external data remain pinned. Using the low-level native NumPy/ORT API is not an incomplete text-only substitute. The broader CLI imports Torch/torchaudio and is not falsely represented as a no-Torch entrypoint.
  • Original target text is fixed English. Reader must provide own/authorized clean speech; no provided identity or recording is copied. Native Nano uses reference codes and target text without a separate reference-transcript field. Robust normalization, long-text chunking and arbitrary scripts are outside this bounded raw-token path.
  • Greedy mode is a source-supported choice differing from default fixed sampling. Native repetition penalty 1.2 remains. No measured naturalness, speaker similarity, sample-rate quality, realtime factor or benchmark transfer is claimed.
  • Native decoded arrays must be finite/nonzero. PCM16 conversion clips values outside[-1,1] and reports clipped sample count; listening and loudness assessment remain necessary. No generated WAV, actual reference, listening result or successful native inference is supplied here.
  • 32 GiB RAM, 40 GiB disk and four threads are selected allocations; full session/cache/codec load fit and performance remain unknown. Official file sizes are not runtime memory or a measured minimum.
  • Reader-authored serial HTTPServer uses private key/loopback, bounded strict fixed JSON and WAV/output checks, but no inference deadline or public multiuser-production guarantee. Reference/voice files remain private reader data.
  • Fake WAV tests validate protocol/security/output boundaries only, not native ONNX compatibility, codec correctness, speech content or voice consent. Native code/model/source pins remain unchanged.
Native component/provider/session error
Stop on hash, metadata, ONNX load or provider errors. Use exact 18-file layout/native module and CPUExecutionProvider; do not float to main, omit codec graphs or replace the full speech path with a text-only endpoint.
Reference format, code shape or budget error
Use your own/authorized 1–5sec 48 kHz PCM16 mono/stereo source and exact reference conversion/hash. Listen to clean natural speech first. Do not substitute synthetic tones or silently crop/reduce codebooks; shorter authorized recording may be needed to fit this chosen row bound.
401 or rejected request
Use matching private 64-character key and exact fixed selector. Extra text, audio, URLs, files and transcript fields are rejected.
Capped, silent, nonfinite or unintelligible speech
Treat cap-length/empty/nonfinite as failure and inspect actual audio/reference. A valid WAV can be wrong or sound poor; record the defect. This guide promises no native quality or speed. Do not remove completion guards or use someone else’s voice to force a demo.

Stop the model

  1. 1. Stop the private speech service

    Press Ctrl+C in the foreground terminal and unset client key. Close optional SSH tunnel; Python shutdown does not stop EC2 billing.

    Command
    unset MOSS_KEY

    Expected result: Native sessions and endpoint stop; reader reference/output files remain until removed.

Uninstall

  1. 1. Remove the dedicated private experiment

    Preserve desired authorized outputs, source/license and audit records first. Confirm directory before deletion. Remove only these selected model caches if unused by other work; shared Ubuntu packages remain. Deleting files is not a promise of forensic erasure.

    Command
    rm -r -- "$HOME/moss-nano-speech"
    rm -r -- "$HOME/.cache/huggingface/hub/models--OpenMOSS-Team--MOSS-TTS-Nano-100M-ONNX"
    rm -r -- "$HOME/.cache/huggingface/hub/models--OpenMOSS-Team--MOSS-Audio-Tokenizer-Nano-ONNX"

    Expected result: The dedicated native checkout, virtual environment, model bundle, reference recording, outputs, private key and selected model caches are removed if present.

Shut down cloud resources

  1. 1. End cloud billing resources

    In EC2 us-east-1, stop for later reuse or terminate this exact VM. Review Delete on termination for its root volume. Delete unwanted detached EBS volumes, snapshots and reserved/Elastic/public IP resources. Stopped VM storage and retained resources may still bill. Check Bills/Cost Explorer later for final usage.

    Expected result: Exact instance is stopped/terminated as intended; no unwanted attached/retained billed resources remain. Instance compute stops only after its lifecycle transition, not when the Python service stops.

Sources and verification

Sources checked Oct 16, 2026. Verification source

Compare this configuration

Pricing and access

No verified hosted per-token price is available for this repository listing. Self-hosted inference has hardware and infrastructure costs; deployment and licensing vary by checkpoint.

OpenMOSS logo
OpenMOSSopen source

Open research for LLMs and multimodal foundation models

Founded 2024Shanghai, China
View full profile