A small FastAPI app that proxies near.ai for audio
transcription, chat, and a tool-using agent, packaged to run inside a
Phala Confidential VM (TDX). The NEAR_API_KEY is
supplied to the CVM as a sealed environment variable — it is never baked into
the image.
GET /— static demo UI (static/index.html)POST /api/transcribe— multipartfile=<audio>; decoded to 16 kHz mono viaffmpeg, split into ≤45 s windows (near.ai 502s past ~60 s/2 MB), each sent towhisper-large-v3and concatenated →{text, duration, segments:<count>}POST /api/transcribe_diarized— same, plus in-CVM speaker diarization (sherpa-onnx, pyannotesegmentation-3.0+ wespeaker embeddings, CPU/ONNX, no network) merged onto the transcript by time overlap →{text, duration, segments:[{start,end,speaker,text}]}POST /api/chat—{messages, model?}→ chat completionPOST /api/agent—{messages, model?}→ tool-using loop (get_current_time,save_note,list_notes)
Diarization models are baked into the image at build time (see Dockerfile) so they are covered by the CVM image attestation and nothing is fetched at runtime. Audio is diarized inside the enclave; only per-window audio leaves the CVM, to the near.ai TEE for ASR.
export NEAR_API_KEY=sk-...
uv venv && uv pip install -r requirements.txt # or: pip install -r requirements.txt
./run.sh # serves on 127.0.0.1:8000Requires ffmpeg on the host. For diarization, the two ONNX models must exist
under models/ (the Dockerfile downloads them at build; for local runs fetch
the same two files into models/segmentation.onnx and models/embedding.onnx).
docker build -t ghcr.io/amiller/teexai-transcribe:latest .
docker push ghcr.io/amiller/teexai-transcribe:latestThe image must be pullable by the Phala node (public, or with pull credentials).
phala deploy -n teexai-transcribe -c docker-compose.yml -e NEAR_API_KEY="$NEAR_API_KEY" --waitRedeploy after pushing a new image:
phala deploy --cvm-id <CVM_ID> -c docker-compose.ymldocker-compose.yml references the published image and reads NEAR_API_KEY
from the sealed env injected by Phala.
.env,service-account.json, and any*.jsonare gitignored — do not commit secrets.- The
Dockerfilecopies onlyapp.py,requirements.txt, andstatic/, so nothing else in the working tree ends up in the image.