EchoScribe records conversations from a Discord voice channel and produces per-participant audio, a structured transcript, and a readable plain-text transcript. It works for tabletop role-playing, meetings, lectures, symposia, interviews, and other spoken sessions.
This guide describes the current application as an operator uses it. It is standalone documentation: it does not depend on the project specification, architecture record, or implementation history.
For normal use, one command performs the complete workflow:
join the configured Discord voice channel
-> record until Ctrl-C
-> finalise one aligned FLAC track per Discord user
-> build chronological transcription work items
-> run faster-whisper locally
-> publish structured and plain-text transcripts
EchoScribe also retains packet, playout, identity, and workflow evidence. If a derived FLAC track is damaged, it can normally be regenerated without asking Discord to provide the session again.
Known failures stop at a safe boundary. Recovery and continuation are explicit operator actions; EchoScribe does not respond to a fault by automatically applying more load or silently skipping data.
The current application provides:
- Discord voice capture through Serenity and Songbird;
- durable recording journals;
- live, session-aligned, per-user FLAC tracks;
- deterministic track recovery from the journals;
- chronological faster-whisper transcription;
- resumable transcription with durable progress;
- complete, atomic retranscription of a finished session;
- per-participant transcription exclusion without suppressing recording;
- offline replay of selected transcription ranges for diagnosis;
- JSONL structured results and a readable text transcript.
It does not currently provide live transcription, relevance filtering, automated report or minutes generation, live facilitation assistance, a graphical interface, or cloud transcription.
You need a Discord bot which can see and connect to the chosen voice channel. The configuration requires:
- the bot token;
- the guild (server) ID;
- the voice-channel ID.
EchoScribe requests guild and guild-voice-state gateway intents. It does not request message content.
Discord IDs are decimal numbers, but they are written as quoted strings in the TOML configuration. Discord's Developer Mode exposes the Copy ID commands used to obtain them.
The repository is a Rust 2024-edition Cargo application. Install a current
stable Rust toolchain, including Cargo. Build products are generated beneath
target/; no executable is copied into the repository root.
Transcription uses a subordinate Python worker. Create one virtual environment at the repository root and install the pinned worker dependencies.
POSIX:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements.txtWindows PowerShell:
py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txtThe installed runtime includes faster-whisper, CTranslate2, SoundFile, and the
Hugging Face Hub client. The Hub client supplies the hf command; it is an
indirect faster-whisper dependency rather than a separate EchoScribe
requirement. Confirm that it is available after activating the virtual
environment with:
hf versionSoundFile extracts each bounded FLAC range supplied to Whisper; it is not an alternative recorder. The same environment runs the optional diagnostic replay helper described in section 10.15.
With device = "cuda", CTranslate2 also needs a compatible local NVIDIA/CUDA
runtime. CPU operation can instead use settings such as device = "cpu" and a
suitable CPU compute type. EchoScribe passes model, device, compute type, and
beam settings to faster-whisper rather than trying to repair an incompatible
runtime.
The public model is downloaded into the normal Hugging Face cache when
faster-whisper first needs it, unless it is already cached. A Hugging Face login
is not normally required for the public model used by the example. To fetch the
example's large-v3 model before the first transcription, use:
hf download Systran/faster-whisper-large-v3This populates the same cache used by faster-whisper. See Appendix A for model selection, download-size checks, and alternative model names.
EchoScribe selects the worker interpreter in this order:
ECHOSCRIBE_PYTHON, when set to a non-empty value;.venv/Scripts/python.exeon Windows or.venv/bin/pythonon POSIX;pythonon Windows orpython3on POSIX.
The root virtual environment and worker script are resolved from the
application repository, independently of the shell's current directory. An
explicitly empty ECHOSCRIBE_PYTHON is an error.
Build the release executable with:
cargo build --releaseThe result is:
target/release/echoscribe # POSIX
target/release/echoscribe.exe # Windows
The root launchers are the normal convenient entry points:
.\echoscribe.ps1./echoscribe.shBoth launchers preserve the caller's working directory, forward every argument unchanged, preserve the application exit status, and invoke the release profile through Cargo. Cargo rebuilds when the source is newer than the executable.
During development, the equivalent direct command is:
cargo run --release -- <arguments>Examples in the rest of this guide use the logical executable name
echoscribe. Substitute the PowerShell launcher, shell launcher, or generated
binary as appropriate.
Copy the examples before the first run.
POSIX:
cp echoscribe.example.toml echoscribe.toml
cp participants.example.toml participants.toml
cp vocabulary.example.txt vocabulary.txtWindows PowerShell:
Copy-Item echoscribe.example.toml echoscribe.toml
Copy-Item participants.example.toml participants.toml
Copy-Item vocabulary.example.txt vocabulary.txtechoscribe.toml, participants.toml, and vocabulary.txt are ignored by
Git. The examples remain tracked.
The complete version-1 schema is:
version = 1
[discord]
token = "replace-with-discord-bot-token"
guild_id = "replace-with-discord-guild-id"
channel_id = "replace-with-discord-voice-channel-id"
[recording]
output_directory = "recordings"
diagnostic_wav = false
[participants]
file = "participants.toml"
[transcription]
model = "large-v3"
language = "en"
device = "cuda"
compute_type = "float16"
beam_size = 5
vocabulary_file = "vocabulary.txt"
resume_rewind_seconds = 120
lexical_no_speech_threshold = 0.60
[segmentation]
vad_enabled = false
merge_gap_ms = 750Unknown or misspelt fields are rejected instead of being silently ignored.
| Field | Meaning |
|---|---|
version |
Configuration format. The current value is 1. |
discord.token |
Bot token used only by live recording. Keep it private. |
discord.guild_id |
Guild containing the voice channel. |
discord.channel_id |
Voice channel EchoScribe joins. |
recording.output_directory |
Parent directory for new session directories. |
recording.diagnostic_wav |
Opt-in SSRC-keyed live WAV diagnostics. Defaults to false if omitted. |
participants.file |
Operator-maintained participant context TOML. |
transcription.model |
faster-whisper model name or model path understood by faster-whisper. |
transcription.language |
Language passed to faster-whisper. |
transcription.device |
CTranslate2 device, normally cuda or cpu. |
transcription.compute_type |
CTranslate2 compute type, such as float16 for the configured GPU. |
transcription.beam_size |
Whisper beam size; must be greater than zero. |
transcription.vocabulary_file |
Session-specific hotword phrase file. |
transcription.resume_rewind_seconds |
Amount of committed transcription reconsidered after a known worker failure. 0 disables rewind. |
transcription.lexical_no_speech_threshold |
Threshold for the unprompted lexical-speech qualification pass. Defaults to 0.60. |
segmentation.vad_enabled |
When true, admit complete work-item ranges through bundled Silero VAD before lexical qualification. When false, send every work item directly to lexical qualification. |
segmentation.merge_gap_ms |
Playout gaps no larger than this are merged for the same user. Must be greater than zero. |
output_directory, the participant file, and the vocabulary file are resolved
relative to the directory containing the named main configuration—not relative
to the shell's working directory.
The current merge_gap_ms = 750 is provisional tuning rather than an
architectural promise.
lexical_no_speech_threshold controls whether an unprompted Whisper segment
is accepted as lexical speech before configured hotwords are used. Higher
values are more permissive; lower values are more restrictive. The 0.60
default is tuned for EchoScribe's source-transcript use case: it retains
useful short responses such as “okay” while deliberately allowing low-value
acknowledgements, abandoned utterances, and speech-like non-verbal sounds to
be rejected.
Offline work-item and transcription commands still require a structurally complete main TOML file. They load only the settings needed for their stage and do not connect to Discord. They also use the immutable participant snapshot inside the session rather than rereading the mutable participant source.
The participant file adds descriptive context keyed by Discord user ID:
version = 2
transcript_name_source = "discord"
[participants."881203221593464864"]
name = "Stefan"
role = "speaker"
[participants."123456789012345678"]
role = "chair"
transcribe = falseEchoScribe obtains the server display name, global display name, and username from Discord. This observed identity is retained as provenance. The participant file supplies:
- optional
name, which may be a real name, character name, professional name, or any other useful alternative to the Discord handle; - optional
role, accepting any non-empty single-line text and defaulting toparticipant; - optional
transcribe, defaulting totrue.
The top-level transcript_name_source selects line attribution. "discord"
is the default and uses the observed Discord name. "name" uses the configured
name, falling back to the Discord name when a participant has no configured
name. The choice does not alter Discord identity evidence.
Empty names or roles, multiline values, and invalid or zero Discord IDs are rejected.
A participant with transcribe = false remains fully recorded in journals,
identity evidence and the routine FLAC track, but is omitted from transcription
work-item generation. This is an operator policy, not a role. A participant who
is absent from the file is recorded and transcribed with default participant
context and Discord-name attribution.
At session creation, EchoScribe writes a canonical participants.toml snapshot
inside the session directory. Later edits to the configured participant file do
not rewrite old sessions. Canonical snapshots always write the resolved Boolean;
older snapshots without it load as transcribe = true. Historical version-1
snapshots retain their original character, player/gm, and Discord-name
semantics; they do not need migration for reading, rebuilding, or
retranscription.
The vocabulary file gives faster-whisper names, places, jargon, scientific terms, and other specialist phrases which ordinary language modelling may miss.
- Use UTF-8 text.
- Put one complete word or phrase on each line.
- Leading and trailing whitespace is removed.
- Blank lines are ignored.
- A line whose first non-whitespace character is
#is a comment. - Inline comments are not supported; a later
#is part of the phrase.
A missing, empty, or comment-only file produces a warning and transcription continues without hotwords. Other read errors and invalid UTF-8 stop the stage.
See vocabulary.example.txt for a commented example.
From the directory containing echoscribe.toml:
echoscribeOr name a different configuration explicitly:
echoscribe path/to/session.tomlEchoScribe creates a session directory, connects to Discord, and joins the configured voice channel. Press Ctrl-C once when the session is over.
Normal shutdown then:
- leaves the voice channel;
- closes and drains accepted capture records;
- finalises healthy routine FLAC encoders;
- publishes
tracks.jsonand recording state; - builds or reuses the work manifest;
- runs one Python transcription worker;
- validates every committed result;
- publishes the final transcript and marks the session complete.
If any required recording or transcription integrity check fails, the command
returns non-zero and stops at the durable boundary described in session.json.
Do not assume that a .flac or transcript-looking file alone proves completion.
To record and finalise without immediately transcribing:
echoscribe record
echoscribe record path/to/session.tomlA healthy recording-only session ends in ready_for_transcription. Advance it
later with:
echoscribe continue recordings/session-... echoscribe.tomlThe launchers preserve the current directory. Therefore, a no-argument launch
looks for echoscribe.toml in the directory from which it was invoked. Pass an
explicit configuration path when running elsewhere.
A complete current session resembles:
recordings/session-.../
├── session.json
├── participants.toml
├── packets.dat
├── playout.dat
├── events.ndjson
├── tracks.json
├── tracks/
│ ├── user-<discord-id>.flac
│ └── ...
└── transcription/
├── worker.lock
├── work-items.jsonl
├── results.jsonl
└── transcript.txt
After supported retranscription, session.json instead references one complete
generation under transcription/retranscriptions/<generation>/. The manifest,
results and readable transcript in that directory become authoritative together;
the previous complete files are not overwritten during staging.
Optional or failure-related output can include:
tracks/user-<discord-id>.flac.part
diagnostics/ssrc-<ssrc>.wav
recovered/ssrc-<ssrc>.wav
transcript.partial.txt
The important roles are:
| Artefact | Role |
|---|---|
session.json |
Current workflow authority, artefact references, failures, and checkpoints. |
participants.toml |
Immutable session-local participant context. |
packets.dat |
Decrypted packet journal used for recording recovery. |
playout.dat |
Authoritative playout and loss decisions. |
events.ndjson |
SSRC mapping, identity, disconnect, and routing-failure evidence. |
tracks.json |
Current per-user routine-track manifest. |
tracks/user-<id>.flac |
Complete session-aligned routine audio for one Discord user. |
*.flac.part |
Incomplete track. It must not be treated as healthy merely because it decodes. |
work-items.jsonl |
Deterministic chronological transcription plan. |
results.jsonl |
Authoritative committed structured transcript. |
transcript.partial.txt |
Rebuildable incomplete human-readable view. |
transcript.txt |
Final rebuildable human-readable transcript. |
worker.lock |
Incidental operating-system coordination file, not evidence that a worker is currently alive. |
Every routine FLAC is mono, 48 kHz, and sourced from 16-bit PCM. Silence is inserted so tracks share one session timeline.
Do not delete the journals merely because the FLACs or transcript exist if you want to retain recording-recovery capability.
The current format-2 readable transcript begins with a participant roster, followed by one completed work item per line:
Participants:
- Discord: Tromador | Name: Stefan | Role: attendee
Transcript:
[00:09:26] Tromador: What were you saying? I completely missed it.
The roster lists transcribed participants in order of first appearance and shows the observed Discord name, the configured name (or the Discord name as fallback), and the role. Participants excluded from transcription have no work items and therefore do not appear in the roster.
Historical format-1 transcripts remain valid as timestamped lines without a
roster. rebuild-transcript preserves the grammar declared by session
authority, so rebuilding format 1 remains line-only. A successful replacement
retranscription publishes format 2 with the roster. When that replacement uses
historical format-1 work items, the old character value appears in the
roster's Name column without changing Discord-based line attribution.
Lines are ordered by session-relative start time. Overlapping speakers remain separate lines. The text view omits SSRCs, confidence scores, word-level timing, and relevance judgements.
The results.jsonl path declared by session.json is the durable transcript
authority. Each result
retains work-item identity, sequence, Discord user, speaker metadata, timing,
source range, text, and completion status. If text and JSONL disagree after a
crash, EchoScribe rebuilds the text from the validated JSONL prefix.
The master transcript includes formal contributions, side discussions, jokes, social chatter, and anything else captured. Relevance filtering belongs to a later downstream process.
Faster Whisper is a probabilistic speech-recognition model. Marginal, ambiguous, noisy, or very short audio can occasionally produce plausible but incorrect text.
EchoScribe supplies the configured vocabulary to Faster Whisper as hotword context. This materially improves names, places, jargon, and specialist terminology. On an uncertain range, however, the same context can sometimes turn a short uncertain transcription into a fluent stock phrase learned by the model, such as:
Thank you for watching.Thank you for your support.- generic video-outro or customer-support language.
EchoScribe does not insert these phrases. Their presence alone does not mean that the recording is damaged, session authority is broken, or transcription files are corrupt. Nor should the audio be assumed to contain only silence: the initial decode may itself have produced a plausible but incorrect short hypothesis from uncertain audio.
EchoScribe deliberately does not blacklist stock phrases. A phrase may occasionally be genuine speech, and removing visible text would not establish what the audio actually contained. Disabling vocabulary assistance would also materially reduce accuracy for proper names and specialist terminology.
Participant recordings are retained so the source audio can be reviewed by a person, considered by later language-model processing while producing minutes or another report, or retranscribed in future with an improved Faster Whisper model or a different transcription backend. This is a known limitation of the current transcription backend, not a session-recovery procedure or a data-integrity fault.
session.json records one of these states:
| State | Meaning |
|---|---|
recording |
Live capture was started and has not published final recording disposition. |
recorded_clean |
Track finalisation completed; the later ready-state publication is still pending. |
recorded_incomplete |
Recording stopped with incomplete routine output. |
awaiting_operator |
A known fault or explicit recovery boundary requires operator action. |
ready_for_transcription |
Recording artefacts are healthy enough for work-item generation/transcription. |
transcribing |
Result authority is published; a worker may be active or eligible for controlled restart. |
transcription_failed |
Worker failure evidence is durable, but the second transition to operator state did not complete. |
complete |
Every work item has a matching result and the final transcript was published. |
Use the state and failure records together. Historical failure entries remain as evidence after successful recovery; their continued presence does not by itself mean that the fault remains unresolved.
Runs the normal one-stop workflow. With no argument, the configuration is
echoscribe.toml in the current directory.
Runs live recording and finalisation only. It never builds work items or starts Python.
Reads session metadata and the packet, playout, and event journals without modifying them. It reports journal tails, packet continuity, playout losses, identity mappings, disconnects, and unresolved SSRC abandonments.
Inspection recognises supported legacy format-2 sessions as well as current workflow formats. A clean inspection means the checks it performed passed; it does not independently certify every derived FLAC or transcription artefact.
Fully decodes every routine track marked complete in tracks.json, checks the
FLAC PCM MD5, validates its audio format, and compares decoded length with the
manifest. It is read-only and can be expensive for long sessions.
Routine clean shutdown deliberately does not pay for this whole-file pass.
Regenerates routine per-user FLAC from the authoritative journals.
- With no user IDs, it selects every track currently marked incomplete.
- With IDs, it rebuilds exactly those users, including a currently healthy track when explicitly requested.
If continuation names users whose legacy post-disconnect transport tails need
recovery, pass those user IDs explicitly even when tracks.json marks their
tracks complete. Repeat recovery after upgrading if the named user was last
recovered by an older EchoScribe version; that recovery could not include the
newly recognised tail.
Recovery requires awaiting_operator. It writes through the normal
.flac.part to .flac publication lifecycle, updates tracks.json, records
durable recovery evidence, and remains in awaiting_operator.
It never continues the pipeline automatically.
Validates recording recovery only. It requires a format-3 or format-4
awaiting_operator session without transcription results.
The command replays authoritative activity, requires every decoded frame to be
safely attributable, verifies complete routine tracks, and refuses unresolved
recording-integrity faults. On success it advances to
ready_for_transcription; it does not transcribe because no configuration was
provided.
Continues the one-stop pipeline from the stage established by current durable authority:
- recovered recording: validate recovery, then proceed;
ready_for_transcription: reuse or build work items, then transcribe;transcribing: controlled restart from the contiguous committed prefix, without rewind;- known transcription failure: apply the configured durable rewind and resume;
- accepted manifest or pre-results orchestration failure: retry that boundary without unnecessarily repeating recording recovery validation.
This command never performs track recovery. Run recover first when routine
tracks are incomplete, or explicitly recover any post-disconnect tail users
named by continuation even when their tracks are marked complete.
Builds transcription/work-items.jsonl without running Whisper. It requires
ready_for_transcription, complete routine tracks, healthy required session
artefacts, and safe attribution of all decoded activity.
The command reads segmentation.merge_gap_ms, uses the immutable session-local
participant snapshot, sorts items globally and deterministically, and
atomically replaces any previous manifest. Workflow state remains
ready_for_transcription.
Runs the explicit transcription stage.
- First invocation requires
ready_for_transcriptionand a published work manifest. - Explicit controlled restart accepts
transcribing, validates the contiguous result prefix, rebuilds partial text, and resumes at the next item without rewind.
After a known worker failure which has moved to awaiting_operator or
transcription_failed, use configured continue so the durable rewind and
failure protocol is applied.
When segmentation.vad_enabled = true, the worker checks each complete
extracted range with the Silero implementation bundled by the pinned
faster-whisper package. A detected speech range is admitted in full to lexical
qualification: VAD does not trim its beginning or end. A Silero-negative range
is not sent to Whisper. It still receives a normal complete results.jsonl
record with empty text so restart ordering remains intact, but no blank
Speaker: line is written to the human transcript.
A Silero-positive range next receives the unprompted lexical qualification
described by lexical_no_speech_threshold. Configured hotwords are used only
after that qualification accepts the range. The worker prints aggregate VAD
accepted/rejected and lexical decision summaries on successful completion.
VAD loading or inference failure stops the worker rather than silently
disabling the gate.
Runs a complete replacement transcription of a healthy complete session.
It validates the existing complete transcript and recording evidence, rebuilds
work items with the current merge_gap_ms and immutable session participant
snapshot, and starts the normal production worker at sequence 1 with the current
transcription settings.
The new manifest, results and readable transcript are staged in a separate
generation. One atomic session.json replacement publishes all three paths
together only after every result and the final transcript validate. A worker,
validation or publication failure leaves the old complete set authoritative and
the workflow state remains complete; retranscription is not continuation or
failure recovery.
To exclude someone from an existing completed session, apply the explicit historical-policy migration and then retranscribe:
echoscribe set-transcription-policy \
recordings/session-... \
123456789012345678 false
echoscribe retranscribe \
recordings/session-... \
echoscribe.tomlDo not edit the session-local participants.toml by hand. The policy command
acquires the session lease and changes only that participant's materialised
transcribe value. Editing the operator's source participant file affects
future sessions only.
Explicitly migrates only a completed session snapshot's transcription policy.
It does not rebuild work items or start transcription; run retranscribe
afterwards. If the user has no snapshot entry, EchoScribe adds one with normal
format-appropriate defaults and the requested policy. This exceptional command is the
supported route for historical snapshots, which otherwise remain immutable.
Reconstructs the session-declared readable transcript atomically for a complete format-5 or format-6 session. It validates the complete work and result authorities, does not start Python, does not change structured results, and does not change workflow state.
Because structured transcript authority is sufficient for rendering, old journals, participant snapshots, track manifests, and FLACs are not required by this command.
Replays packet and playout journals into SSRC-keyed diagnostic WAV files under
recovered/. This is diagnostic reconstruction, not routine user-track
recovery. The output directory must not already exist.
Runs the older diagnostic/migration path which replays journals into
SSRC-keyed, fully decode-verified FLAC files and writes its export manifest.
It is not the routine recovery command and is not suitable for writing over a
current session's existing tracks/ directory. Use recover for current
Discord-user-keyed routine tracks.
workers/faster-whisper/diagnose_ranges.py replays selected existing work
items without changing workflow state, transcript authority, or published
session artefacts. It is a diagnostic helper rather than an echoscribe
subcommand.
Use the same Python environment as the production worker. Repeat --sequence
to inspect several items in one model load:
./.venv/bin/python workers/faster-whisper/diagnose_ranges.py \
--config echoscribe.toml \
--session recordings/session-... \
--sequence 17 \
--sequence 42 \
--output diagnostics/session-ranges.jsonlPowerShell:
.\.venv\Scripts\python.exe workers\faster-whisper\diagnose_ranges.py `
--config echoscribe.toml `
--session recordings\session-... `
--sequence 17 `
--sequence 42 `
--output diagnostics\session-ranges.jsonlFor each range, the helper prints acoustic measurements, padded and unpadded Silero results, and five decode comparisons in this output order:
current_echoscribeuses the current hotword-assisted decode settings on the original complete range.no_hotword_controluses the same original range without hotwords.craig_like_internal_vadenables selected Craig-like internal-VAD behaviour and word timestamps while retaining hotwords.internal_vad_no_hotwordsuses the original complete range with internal VAD and word timestamps enabled, but hotwords disabled.explicit_silero_trimmedexplicitly decodes the concatenated unpadded Silero speech spans and remains distinct from Faster Whisper's internal VAD.
The Craig-like comparison tests selected behaviour from
CraigChat/runpod-worker-faster_whisper
only; it is not an exact reproduction of Craig's private production
configuration. Likewise, the result labelled current_echoscribe is the
current prompted decode stage, not a replay of the complete Silero and lexical
admission pipeline. These comparisons gather evidence; none is presented as a
recommended production replacement.
--output is optional. When supplied, it is overwritten with one JSON object
per requested work item. The repository-root diagnostics/ directory is
ignored by Git and is a suitable destination; create it first if it does not
exist. Diagnostic JSONL is evidence for investigation, not session or
transcript authority. The helper loads the model once, performs one untimed
warm-up, and cleans its temporary bounded WAV files.
-
Read the command error and shutdown telemetry.
-
Inspect the session:
echoscribe inspect recordings/session-...
-
Correct the external cause first, such as disk capacity or resource pressure.
-
Recover all incomplete tracks or selected users. If continuation reports a post-disconnect transport tail, name every reported user explicitly even if the track is currently marked complete:
echoscribe recover recordings/session-... echoscribe recover recordings/session-... 881203221593464864
-
Optionally perform full FLAC verification:
echoscribe verify recordings/session-...
-
Validate and resume the pipeline:
echoscribe continue recordings/session-... echoscribe.toml
Authoritative journal loss or corruption cannot be repaired by regenerating a FLAC. Continuation will continue to refuse while that fault remains.
Committed results are preserved. After correcting the cause—such as the Python environment, model cache, CUDA runtime, or available storage—run:
echoscribe continue recordings/session-... echoscribe.tomlEchoScribe validates the result prefix and applies
resume_rewind_seconds. The rewind removes one contiguous suffix beginning at
the earliest committed result which overlaps the rewind window. Crash-safe
prepared/applied checkpoints prevent repeated attempts from progressively
discarding older results when no new progress was made.
There is no automatic retry.
If durable state remains transcribing, configured continue performs the
stage-aware controlled restart. The explicit equivalent is:
echoscribe transcribe recordings/session-... echoscribe.tomlThis route retains the valid contiguous prefix and resumes at the next item without failure rewind.
If the session is complete and work-items.jsonl plus results.jsonl remain
intact:
echoscribe rebuild-transcript recordings/session-...No audio is decoded or retranscribed.
Only one mutating offline operation may own a session at a time. Recovery,
continuation, work-item generation, transcription, retranscription, participant
policy migration, and transcript rebuilding share an operating-system file lock
at transcription/worker.lock.
If EchoScribe reports that another mutating operation owns the session:
- check whether a Rust or Python EchoScribe process is still running;
- wait for it to finish or investigate it;
- do not delete
worker.lockas a remedy.
The file can remain after a clean exit. Ownership is the live operating-system lock, not the file's existence. A Python worker inherits the locked handle, so it continues to exclude a second writer even if its Rust parent dies. The lock becomes available when the final owning process terminates.
Read-only inspect and verify do not take exclusive ownership.
At shutdown, EchoScribe reports:
- capture queue accepted/consumed records and drops;
- packet, event, playout, audio, and routing-tick counts;
- capture queue high-water mark;
- journal checkpoint and storage-sync counts;
- identity-routing resolutions and abandonments;
- live FLAC queue depth/high-water/failures;
- per-user FLAC frame, sample, silence, and SSRC information;
- RTP sequence gaps, duplicates, and late/out-of-order packets.
Zero drops and a low high-water mark are healthy evidence. Any authoritative queue drop, decoded-audio ingress drop, unresolved identity abandonment, FLAC queue rejection, or writer failure prevents a clean recording disposition.
Diagnostic WAV is optional and failure-isolated. Enabling it may help codec or alignment investigation, but it is not required for normal recording or recovery.
EchoScribe rejects missing and unknown fields. Compare the live file with
echoscribe.example.toml; do not assume a successful cargo check validates
operator configuration.
Check the top-level version, transcript_name_source, quoted Discord-ID table
keys, and non-empty single-line names and roles. Version 2 accepts arbitrary
roles; version-1 historical files retain their player/gm validation.
This is non-fatal. Copy vocabulary.example.txt, create an empty
vocabulary.txt, or deliberately continue without hotwords.
Confirm the repository still contains
workers/faster-whisper/transcription_worker.py, then check the interpreter
selection described in section 3.4. An explicitly empty
ECHOSCRIBE_PYTHON is rejected.
Activate the root .venv and install requirements.txt. Confirm EchoScribe is
selecting that interpreter rather than a different system Python.
The worker exits non-zero and EchoScribe retains committed progress. Correct
the model cache, driver, CUDA libraries, device, or compute-type setting, then
use configured continue. Do not edit results.jsonl to make the error vanish.
Read session.json failures and checkpoints, inspect journal health, and check
tracks.json. Use recording recovery only for derived track faults. Use
configured continuation for transcription or post-recording stage failures.
The named user's current FLAC may be marked complete but still predate tail-aware recovery. Rebuild each user named by the error, then retry the same continuation command:
echoscribe recover recordings/session-... <reported-user-id>
echoscribe continue recordings/session-... echoscribe.tomlRepeat the recovery even if that user was recovered before EchoScribe gained post-disconnect tail attribution. Older generic recovery evidence does not prove that the tail is present in the FLAC.
Playability is not completeness. The .part suffix and tracks.json state are
the relevant disposition; recover the track before continuation.
JSONL is authoritative. Controlled restart reconstructs partial text. For a
complete session, use rebuild-transcript.
An empty structured result may have been rejected by Silero or by the unprompted lexical qualification pass. Identify the corresponding sequence in the session-declared work manifest and use the diagnostic replay helper in section 10.15. Do not edit the result or transcript files to force acceptance.
The main configuration contains a Discord bot token and should remain private. Participant snapshots and event journals contain Discord user IDs and display identity evidence. Transcripts contain everything captured, including social conversation which may be unrelated to the game.
Diagnostic replay JSONL can contain the same participant identity, source paths, decoded text, confidence evidence, and timing information. Handle it as session data even when it is written outside the session directory.
For a recoverable backup, preserve the entire session directory. In particular,
retain session.json, all three journals, the participant snapshot, and event
evidence together. Copying only the final transcript preserves the readable
product but not recording recovery or structured provenance.
Session artefact paths recorded in session.json are relative to the session
directory. EchoScribe rejects absolute paths and paths which escape it.
The pre-rewrite Discord.js/Python capture, filtering, VAD-rescue,
transcription, and deduplication stack is preserved under
archive/legacy-pipeline/. It is historical reference material and is not
invoked by the current Rust application.
See the legacy directory's own README and detailed pipeline document for its operation. Do not install or invoke legacy requirements as part of the current workflow.
The example configuration uses large-v3. It is a sound starting point when
the available GPU and acceptable processing time can accommodate it. Smaller
models require fewer resources and generally run faster, but transcription
accuracy may fall. The practical result depends on the language, recording,
hardware, and CTranslate2 compute type, so test alternatives against
representative EchoScribe recordings before adopting one.
Common faster-whisper model names include:
transcription.model |
Intended use |
|---|---|
large-v3 |
Full multilingual model and the EchoScribe example setting. |
medium, small, base, tiny |
Progressively smaller multilingual models. |
medium.en, small.en, base.en, tiny.en |
English-only variants; the benefit is greatest for the smallest models. |
turbo |
Faster large-v3-derived model with a possible accuracy trade-off. |
distil-large-v3 |
Faster distilled English model; evaluate it as a different model rather than assuming identical output. |
The Faster Whisper project documents the model names it supports and its own performance measurements. The OpenAI Whisper model table gives the underlying model families, approximate scale, language coverage, and relative speed. Its memory figures describe OpenAI Whisper rather than CTranslate2, so use them as model-selection guidance rather than an exact EchoScribe hardware requirement.
transcription.model may also name a Hugging Face repository containing a
CTranslate2-converted Whisper model, or a local converted-model directory.
An arbitrary original Transformers checkpoint is not necessarily directly
loadable by faster-whisper; see its
model-conversion guidance.
Activate EchoScribe's virtual environment first. Installing the pinned Python
requirements installs huggingface-hub, which provides hf:
hf versionFaster-whisper downloads a recognised model name automatically on first use.
For the example configuration, it maps large-v3 to the public
Systran/faster-whisper-large-v3 repository. Pre-download it with:
hf download Systran/faster-whisper-large-v3The command downloads the repository into the normal Hugging Face cache and prints the cached snapshot path. Repeating it reuses files already present. Authentication is not normally required for this public repository.
To inspect the transfer without downloading anything, including the amount not already cached, use:
hf download Systran/faster-whisper-large-v3 --dry-runTo inspect or verify the cache later, use:
hf cache ls
hf cache verify Systran/faster-whisper-large-v3The Hugging Face CLI guide
documents download, authentication, cache-location, and cache-management
options. Avoid copying cached snapshot files by hand: the cache is
version-aware and should be managed through faster-whisper or hf.