cassini is the product CLI for recording Nextcloud Talk meetings into one
portable meeting file that can later be loaded in the web app.
The normal user flow is:
- point Cassini at a meeting
- let Cassini record and process it
- end up with one portable
.opusfile
Cassini still uses .run, .meeting, and .site artifacts internally, but
those are now implementation detail and debugging surfaces rather than the main
product story.
From this source checkout, use:
./bin/cassiniThat wrapper builds the current cassini CLI from the Go module and runs it
from your current working directory.
See what the product exposes:
./bin/cassini --helpValidate the environment before expensive work starts:
./bin/cassini doctorIf doctor reports an unwritable Cassini cache or model directory,
fix that path or point Cassini at a writable cache root before building:
export CASSINI_CACHE_ROOT="$PWD/.cache/cassini"
./bin/cassini doctorSet your Talk room URL:
export CALL_URL="https://cloud.example.com/call/<ROOM_TOKEN>"Record a meeting and let Cassini finish with one portable file:
./bin/cassini record --call "$CALL_URL" --out "./My Meetings/2026-03-11 Weekly Sync.opus"If Cassini fails after capture or during processing, fix the issue and rerun the
same command with the same --out path. Cassini now keeps resumable state in a
hidden .cassini-work/ directory next to the target file and will reuse the
finished recording or finished meeting artifact when possible.
Inspect the resulting file:
./bin/cassini inspect "./My Meetings/2026-03-11 Weekly Sync.opus"If you already have an existing recording, build the portable file from that:
./bin/cassini build /path/to/meeting.mkv --out "./My Meetings/Imported Meeting.opus"The user-facing portable-file path currently needs a real meeting.
For local smoke and diagnostics, simulate mode still writes a debug .run
bundle:
./bin/cassini record --simulate --out ./runs/demo.run
./bin/cassini inspect ./runs/demo.runBrowse an already-generated sample site from this checkout:
./bin/cassini serve ./cassini-viewer/exports/static-meetingsIf you have processed .opus recordings, build a browser view of them in one step:
cd cassini-viewer
npm install
npm run build
node ./scripts/export-static-meetings.mjs \
--source-dir /path/to/your/processed-opus \
--output-dir /tmp/cassini-opus-view
cd ..
./bin/cassini serve /tmp/cassini-opus-viewIf you want to keep the internal working artifacts visible, you can still use the explicit pipeline:
./bin/cassini record --call "$CALL_URL" --out ./runs/weekly-sync.run
./bin/cassini build ./runs/weekly-sync.run --out ./meetings/weekly-sync.meeting
./bin/cassini publish ./meetings --out ./site
./bin/cassini serve ./siteInspect any primary Cassini artifact:
./bin/cassini inspect "./My Meetings/2026-03-11 Weekly Sync.opus"
./bin/cassini inspect ./runs/weekly-sync.run
./bin/cassini inspect ./meetings/weekly-sync.meeting
./bin/cassini inspect ./siteThe production install path is the Nextcloud AppAPI ExApp — registration, Talk recording handoff, verification checklist, and GPU setup are covered in:
- License: GNU AGPLv3
- Changelog: CHANGELOG.md
- Security reporting: SECURITY.md
- Contribution notes: CONTRIBUTING.md
For the repo-root Docker Compose deployment bundle (development and staging — not the Nextcloud app install), see:
From there you can bring up the packaged operator, control panel, and viewer with docker compose up --build.
The local stack and showcase/demo flows now live under cassini dev:
./bin/cassini dev stack up
./bin/cassini dev room create --name "Local room"
cp .envrc.example .envrc
direnv allow
./bin/cassini dev smoke
./bin/cassini dev fixture prepare-showcase
./bin/cassini dev player showcase --call-url "$CALL_URL"For local viewer development, pull demo data directly into the viewer dev
server root. Set DEMO_DATA_URL in a gitignored .envrc or export it in your
shell, then run:
cd cassini-viewer
npm install
npm run build
npm run demo-data:pull
npm run devUse npm run demo-data:clean to remove the pulled bundle.
The demo-data pull downloads index.html, referenced assets/*, catalog.json, and each meeting directory into
cassini-viewer/exports/viewer-demo, which is where the Vite dev server already
serves /catalog.json and /meetings/* from. Meeting file names are read from
each meeting's manifest.json, so DEMO_DATA_URL remains the only required
setting.
cassini buildnow uses the native Go transcription pipeline incassini-go-recorder/internal/transcribe.cassini publishcurrently uses the static exporter undercassini-publisher/bin/export-static-meetings.sh.cassini doctorshould be run beforerecord --out ...opusorbuild --out ...opus; it catches cache, media-tool, and runtime issues early.- In this checkout, the current doctor output is expected to fail if the
Cassini cache or model directories are not writable, or if required media
tools like
ffmpeg/ffprobeare unavailable.
./bin/cassini doctor
./bin/cassini record
./bin/cassini build
./bin/cassini publish
./bin/cassini serve
./bin/cassini inspect
./bin/cassini meetings
cassini meetings reads published recordings back out of Nextcloud as a given
Nextcloud user — see Agent access to meeting recordings.
Older wrappers under directories such as:
cassini-recorder/cassini-diagnostics/cassini-publisher/cassini-lab/cassini-player/
are now legacy shims or implementation surfaces. They are being deprecated in
favor of ./bin/cassini.
The local harness implementation now lives under harness/, and it is now
being surfaced through ./bin/cassini dev ... rather than taught as a peer
product.
- Start here: docs/README.md — curated index (concepts, install & configure, reference)
- System overview: docs/architecture.md
- Cross-cutting reference: docs/portable-meeting-format.md, docs/audio-glossary.md
- Component deep-dives: cassini-go-recorder/docs/ (active transcription lives here), cassini-viewer/docs/