docs: add README and initial docs scaffold

This commit is contained in:
pyr0ball 2026-07-11 22:41:41 -07:00
parent b92285b4b3
commit cefec5cb68
16 changed files with 576 additions and 1 deletions

View file

@ -1,6 +1,6 @@
# Linnet
> AI for the tasks the system made hard on purpose.
> Tools for the tasks the system made hard on purpose.
**Real-time voice transcription with tone annotation — tonal subtext labels for calls, appointments, and difficult conversations.**

View file

@ -0,0 +1,45 @@
# Installation
Linnet requires [Docker](https://docs.docker.com/get-docker/) and Docker Compose.
## Step 1: Clone the repo
```bash
git clone https://git.opensourcesolarpunk.com/Circuit-Forge/linnet
cd linnet
```
## Step 2: Configure
```bash
cp .env.example .env
```
Open `.env` and review the defaults. Most settings work out of the box. See [Local Model Setup](llm-setup.md) for optional translation configuration.
## Step 3: Start
```bash
./manage.sh start
```
Open [http://localhost:8521](http://localhost:8521) in your browser.
The API runs at [http://localhost:8522](http://localhost:8522).
## manage.sh commands
| Command | What it does |
|---------|-------------|
| `./manage.sh start` | Start API and frontend |
| `./manage.sh stop` | Stop all services |
| `./manage.sh restart` | Stop then start |
| `./manage.sh status` | Show running services |
| `./manage.sh logs` | Tail combined logs |
| `./manage.sh open` | Open UI in default browser |
## First run
The first start downloads Whisper and wav2vec2 model weights. This takes a few minutes depending on your connection. Subsequent starts use the cached models.
Once the UI opens, grant microphone access when prompted and click **New session** to start transcribing.

View file

@ -0,0 +1,40 @@
# Local Model Setup
Linnet runs fully locally on the Free tier. The transcription pipeline (Whisper) and tone annotation engine (wav2vec2-based SER, or speech emotion recognition) are bundled and download automatically on first start. No additional setup is required for core functionality.
## What runs locally
| Component | Model | Purpose |
|-----------|-------|---------|
| Transcription | Whisper (base or small) | Speech-to-text |
| Tone annotation | wav2vec2 SER | Emotional/social register classification |
Audio never leaves your machine. Both models run on-device via the `cf-voice` inference layer.
## Optional: translation via DeepL
Translation is the only feature that requires an external service. Two paths are available:
### Free tier: BYOK (bring your own key)
[DeepL offers a free API tier](https://www.deepl.com/en/pro/change-plan) (500,000 characters/month). Sign up, copy your API key, and add it to your `.env`:
```dotenv
DEEPL_API_KEY=your-deepl-api-key-here
```
Restart Linnet after saving the file.
### Paid tier
The Paid tier includes a CF-managed DeepL key. No environment variable is needed. Translation appears automatically once your license key is active.
## Choosing a Whisper model size
The default is `whisper-base`. If your machine has more VRAM or you want higher accuracy:
```dotenv
WHISPER_MODEL=small # default: base
```
Available sizes: `tiny`, `base`, `small`, `medium`, `large`. Larger models are slower but more accurate, especially for accented speech.

40
docs/index.md Normal file
View file

@ -0,0 +1,40 @@
# Linnet
Real-time voice transcription with tone annotation for calls, appointments, and difficult conversations. Linnet labels not just what is being said, but how it is being said — giving neurodivergent (ND) users a way to track tonal subtext without spending cognitive bandwidth decoding social register in real time.
## Features
- **Real-time transcription** — local [Whisper](https://github.com/openai/whisper) inference; no audio leaves your machine
- **Tone annotation** — each speech segment is labeled with its emotional and social register (Apologetic, Stressed, Enthusiastically agreeing, Passive-aggressively complying, and more)
- **Elcor mode** — bracketed tone-prefix format that makes register explicit inline with transcript text
- **Speaker diarization** — distinguishes speakers in multi-participant calls
- **Session history** — browse, search, and export past sessions; everything stays local
- **Translation** — via DeepL; BYOK (bring your own key) on Free tier, CF-managed key on Paid tier
- **Corrections** — inline widget to fix transcript errors; corrections retrain your local model over time
- **Meeting notes export** — structured export of transcript plus tone timeline for post-call review
- **Embeddable widget** — overlay architecture designed to sit on top of video call windows
## Tiers
| Feature | Free | Paid |
|---------|------|------|
| Local transcription (Whisper) | Yes | Yes |
| Tone annotation (local SER) | Yes | Yes |
| Elcor mode | Yes | Yes |
| Speaker diarization | Yes | Yes |
| Session history (30 days) | Yes | Yes |
| Session history (unlimited) | No | Yes |
| Corrections + local model retraining | Yes | Yes |
| Meeting notes export | Yes | Yes |
| Embeddable widget | Yes | Yes |
| Translation (BYOK DeepL key) | Yes | Yes |
| Translation (CF-managed DeepL key) | No | Yes |
SER = speech emotion recognition. BYOK = bring your own key.
## Quick links
- [Installation](getting-started/installation.md)
- [Live Session guide](user-guide/live-session.md)
- [Source code](https://git.opensourcesolarpunk.com/Circuit-Forge/linnet)
- [All CircuitForge docs](https://docs.circuitforge.tech)

1
docs/plausible.js Normal file
View file

@ -0,0 +1 @@
(function(){var s=document.createElement("script");s.defer=true;s.dataset.domain="docs.circuitforge.tech,circuitforge.tech";s.dataset.api="https://analytics.circuitforge.tech/api/event";s.src="https://analytics.circuitforge.tech/js/script.js";document.head.appendChild(s);})();

51
docs/reference/privacy.md Normal file
View file

@ -0,0 +1,51 @@
# Privacy
Linnet is designed for sensitive conversations. Medical appointments, HR discussions, legal calls, and family situations where the stakes of surveillance are real. Privacy is an architectural constraint, not a policy.
## Audio
**Audio never leaves your machine.** Transcription runs locally via Whisper. Tone annotation runs locally via a wav2vec2-based SER (speech emotion recognition) model. No audio is transmitted anywhere at any point.
This is true on the Free tier and the Paid tier.
## Session data
Session transcripts, tone timelines, speaker maps, and corrections are stored in a local SQLite database on your machine. The database location is configurable via `LINNET_DATA_DIR` in `.env`.
Linnet does not sync, back up, or transmit session data to CircuitForge servers or any third party.
## Telemetry
Linnet does not collect telemetry by default. No usage analytics, no crash reports, no feature tracking. The docs site uses [Plausible Analytics](https://plausible.io) (privacy-preserving, no cookies, no cross-site tracking) but the application itself does not.
## Correction data
Corrections you make to transcripts are stored in a local dataset and used to fine-tune your local Whisper checkpoint. Correction data is never uploaded to CircuitForge. Retraining happens entirely on your hardware.
## Translation
Translation is the only feature that contacts an external server. When you translate a session, transcript text (not audio) is sent to DeepL. DeepL's privacy policy governs how they handle that data.
If your conversations are sensitive enough that even text transmission is a concern, do not use translation.
## What CircuitForge receives
| Data type | Received by CF |
|-----------|---------------|
| Audio | Never |
| Transcript text | Never |
| Tone annotations | Never |
| Session metadata | Never |
| Correction data | Never |
| License key validation | Yes (key hash only, no session content) |
| Docs site visits | Yes (Plausible, aggregated, no PII) |
## Self-hosting and air-gapped use
Linnet is designed to run on your hardware without any outbound connections. For fully air-gapped operation:
- Pre-download the Whisper and wav2vec2 model weights during setup
- Set `DEEPL_API_KEY=` to blank (disables translation)
- Do not configure a license server endpoint
See the [air-gapped deployment guide](https://git.opensourcesolarpunk.com/Circuit-Forge/linnet) in the repo for full offline setup instructions.

View file

@ -0,0 +1,40 @@
# Tier System
Linnet is available on two tiers. All core functionality runs locally on the Free tier.
## Comparison
| Feature | Free | Paid |
|---------|:----:|:----:|
| Real-time transcription (local Whisper) | Yes | Yes |
| Tone annotation (local SER) | Yes | Yes |
| Elcor mode | Yes | Yes |
| Speaker diarization | Yes | Yes |
| Session history | 30 days | Unlimited |
| Corrections + local model retraining | Yes | Yes |
| Meeting notes export (Markdown, plain text, CSV) | Yes | Yes |
| Embeddable widget | Yes | Yes |
| Translation (BYOK: your own DeepL key) | Yes | Yes |
| Translation (CF-managed DeepL key) | No | Yes |
SER = speech emotion recognition. BYOK = bring your own key.
## Free tier
The Free tier is fully functional for personal, non-commercial use. Transcription, tone annotation, Elcor mode, speaker diarization, corrections, and export all work without a license key or an internet connection.
The only limitations are:
- Session history is retained for 30 days
- Translation requires your own DeepL API key (DeepL offers a free tier at 500,000 characters/month)
## Paid tier
The Paid tier adds:
- Unlimited session history retention
- CF-managed DeepL key (translation works without your own key)
## License keys
License keys use the format `CFG-LNNT-XXXX-XXXX-XXXX`. Enter your key in **Settings** then **License**.
Free keys for personal use are available at [circuitforge.tech/software](https://circuitforge.tech/software).

View file

@ -0,0 +1,10 @@
/* Dark mode image treatment — app screenshots are light-mode only, soften them on slate */
[data-md-color-scheme="slate"] img:not([src*=".svg"]) {
filter: brightness(0.82) contrast(1.05);
border-radius: 4px;
}
/* SVGs (icons, diagrams) inherit theme color naturally — no filter needed */
[data-md-color-scheme="slate"] img[src*=".svg"] {
filter: none;
}

View file

@ -0,0 +1,30 @@
# Corrections
Linnet includes an inline correction widget for fixing transcription errors. Corrections are saved locally and used to fine-tune your local Whisper model over time.
## Making a correction
1. Open a session
2. Click any transcript line to enter edit mode
3. Fix the text inline
4. Press **Enter** or click **Save**
The corrected text replaces the original in the session view. The original is preserved in the correction log for model retraining.
## How corrections improve accuracy
Each correction you make is stored in a local dataset. Linnet periodically runs a fine-tuning pass on your local Whisper checkpoint using these corrections, biasing the model toward your voice, speech patterns, and vocabulary.
This is entirely local. Corrections are never sent to CircuitForge servers.
The fine-tuning pass runs in the background when Linnet is idle. You can also trigger it manually from **Settings** under **Model** then **Retrain now**.
## What corrections do not fix
Corrections fix recognition errors (wrong words, missed words). They do not adjust tone labels — tone annotation is based on audio characteristics, not the text content. If a tone label is wrong, that reflects model uncertainty about the audio segment, not a transcription error.
Tone label feedback is on the roadmap as a separate feature.
## Resetting corrections
If you want to discard your local correction dataset and start fresh from the base model, go to **Settings** then **Model** then **Reset correction data**. This removes all saved corrections and resets to the bundled checkpoint.

View file

@ -0,0 +1,35 @@
# Elcor Mode
Elcor mode is an alternative display format where tone labels appear as bracketed prefixes inline with the transcript text, rather than as separate tags.
## The format
Standard view:
> "Sure, that sounds great." `Passive-aggressively complying`
Elcor mode:
> [Passive-aggressively complying] "Sure, that sounds great."
> [With sincerity] "I'll look into that right away."
> [Enthusiastically agreeing] "Absolutely, I think that's the right call."
The name comes from the Elcor species in the Mass Effect series, who verbally prefix every statement with its emotional context because their tonal range is not legible to other species. The parallel is intentional: Linnet makes tonal subtext explicit for people who find it cognitively expensive to decode implicitly.
## When to use Elcor mode
Elcor mode is best for:
- **Reading transcripts after the fact** — the inline format makes tone immediately legible without looking back and forth between text and tag
- **Exporting and sharing** — the bracketed prefix format is readable in plain text, paste-friendly, and does not depend on Linnet's UI
- **Users who prefer sequential reading** — some people find the inline format easier to follow than a parallel tag column
The standard tag view may be preferable during a live session when screen space is limited.
## Toggling Elcor mode
Click the **Elcor** toggle in the session toolbar. The mode applies immediately to the current transcript view and is saved as a preference for future sessions.
You can also toggle per-export: the [Meeting Notes export](meeting-notes-export.md) dialog lets you choose the format (tagged vs. Elcor prefix) independently of your live view preference.

View file

@ -0,0 +1,40 @@
# Live Session
A live session is a real-time transcription with tone annotation running as you speak or listen.
## Starting a session
1. Open Linnet at [http://localhost:8521](http://localhost:8521)
2. Click **New session**
3. Grant microphone access if prompted by the browser
4. Linnet begins transcribing immediately
## What you see
The session view shows a scrolling transcript. Each line includes:
- **Speaker label** — Speaker A, Speaker B, etc. (requires diarization to be enabled; see below)
- **Transcript text** — the spoken words as recognized by Whisper
- **Tone label** — the emotional/social register of that segment, shown as a tag (e.g. `Apologetic`, `Stressed`)
In [Elcor mode](elcor-mode.md), tone labels appear as bracketed prefixes inline with the text instead of as separate tags.
## Speaker diarization
For calls with multiple participants, enable **Diarization** in the session settings before starting. Linnet will assign speaker labels (Speaker A, Speaker B, etc.) based on voice characteristics. Labels can be renamed after the session ends.
Diarization adds a small amount of processing overhead. For single-speaker use (personal recording, dictation), you can leave it off.
## Pausing and resuming
Click **Pause** to stop audio capture temporarily. The transcript is preserved. Click **Resume** to continue. Pausing is useful when you need to focus on the conversation without visual distraction from the scroll.
## Ending a session
Click **End session**. Linnet saves the full transcript, tone timeline, and speaker map to local storage. The session appears in [Session History](session-history.md).
After ending, you can:
- Review the transcript with tone labels
- Export meeting notes (see [Meeting Notes Export](meeting-notes-export.md))
- Make corrections to the transcript (see [Corrections](corrections.md))
- Translate the transcript (see [Translation](translation.md))

View file

@ -0,0 +1,47 @@
# Meeting Notes Export
Meeting notes export produces a structured document combining the session transcript and tone timeline, suitable for review, sharing, or archiving.
## Exporting a session
1. Open a session from [Session History](session-history.md) or immediately after ending a live session
2. Click **Export**
3. Choose your format and options
4. Save the file
## Export formats
### Transcript with tone tags (Markdown)
Each line includes the speaker label, transcript text, and tone tag:
```
**Speaker A** [Reassuring]: "I understand your concerns, and we're going to address them."
**Speaker B** [Skeptical]: "Sure, we'll see."
```
### Elcor format (plain text)
Tone labels appear as inline bracketed prefixes, readable without Markdown rendering:
```
Speaker A [Reassuring]: "I understand your concerns, and we're going to address them."
Speaker B [Skeptical]: "Sure, we'll see."
```
### Tone timeline (CSV)
A tabular export of every segment with speaker, timestamp, transcript text, and tone label. Useful for analysis or importing into other tools.
## Export options
| Option | Description |
|--------|-------------|
| Include tone labels | Toggle tone labels on or off in the export |
| Format | Markdown or plain text (Elcor) for narrative; CSV for data |
| Speaker names | Rename Speaker A, Speaker B, etc. before exporting |
| Time range | Export the full session or a selected time window |
## Where exports are saved
Exports are saved to your local machine via the browser's download prompt. Linnet does not store exports on its own — only the source session remains in local storage.

View file

@ -0,0 +1,37 @@
# Session History
Linnet stores all past sessions locally. Nothing is sent to a server. The session history view lets you browse, search, and re-open any past session.
## Browsing sessions
Click **History** in the sidebar. Sessions are listed in reverse chronological order with:
- Date and time
- Duration
- Speaker count (if diarization was enabled)
- A short excerpt from the transcript
## Searching sessions
Use the search bar to find sessions by keyword. Search runs against the full transcript text. Tone label search is also supported — for example, searching "stressed" surfaces sessions where that label appeared.
## Opening a session
Click any session to open the full transcript view with tone annotations, speaker map, and action buttons (export, correct, translate).
## Storage limits
| Tier | Session retention |
|------|------------------|
| Free | 30 days |
| Paid | Unlimited |
Sessions older than 30 days are deleted automatically on the Free tier. Export sessions you want to keep before they expire.
## Exporting sessions
From any session detail view, use [Meeting Notes Export](meeting-notes-export.md) to save a structured export. Exports are plain text or Markdown and can be saved anywhere on your machine.
## Deleting sessions
Click the **Delete** button on any session card in the history list. Deletion is permanent — Linnet does not keep a trash. If you have a session you want to retain but clear from the history UI, export it first.

View file

@ -0,0 +1,47 @@
# Tone Annotation
Tone annotation labels each speech segment with its emotional and social register — not just what was said, but how it was said.
## Why it matters
Reading tone is cognitively expensive. For autistic and neurodivergent (ND) users, parsing the social subtext of speech in real time can compete with comprehending the content itself. Tone annotation offloads that work: Linnet surfaces the register so you can stay focused on what is being communicated rather than spending bandwidth on how it is being delivered.
This is especially useful in high-stakes conversations where the stakes of misreading tone are high: medical appointments, workplace meetings, negotiations, difficult family calls.
## How it works
Linnet runs a local SER (speech emotion recognition) model — a fine-tuned wav2vec2 checkpoint — against each audio segment as it is transcribed. No audio leaves your machine. The model outputs a register classification, which Linnet maps to a human-readable label.
## Example tone labels
| Label | What it signals |
|-------|----------------|
| Apologetic | The speaker is hedging or expressing regret |
| Stressed | Elevated pitch or pace suggesting pressure |
| Enthusiastically agreeing | Affirmation with genuine energy |
| Passive-aggressively complying | Verbal agreement with tonal resistance |
| Dismissive | Low engagement, clipped delivery |
| Reassuring | Calm, warm, stabilizing register |
| Neutral | No strong register detected |
| Uncertain | Halting, questioning delivery |
| Firm | Definitive, little hedging |
| Frustrated | Compressed, effortful delivery |
The label set is not exhaustive — register is continuous and context-dependent. The model assigns the closest match from the taxonomy.
## Confidence and edge cases
Low-confidence segments (where the model is uncertain about the register) are shown with a lighter label. If a segment has no clear tonal signal, it is labeled **Neutral**.
The model performs best on:
- Clear audio with minimal background noise
- English speech (other languages reduce accuracy)
- Single-speaker segments (diarization helps isolate voices)
Cross-cultural register differences can affect label accuracy — some expressions of politeness read as passive-aggressive to a model trained primarily on Western English corpora. Use tone labels as a signal, not a verdict.
## Viewing tone in context
Tone labels appear as tags next to each transcript line in the default view. Switch to [Elcor mode](elcor-mode.md) to show tone as bracketed prefixes inline with the text.
The full tone timeline is available in the session detail view and is included in [Meeting Notes exports](meeting-notes-export.md).

View file

@ -0,0 +1,41 @@
# Translation
Linnet can translate session transcripts via the DeepL API. Translation is available on all tiers; the difference is where the API key comes from.
## How to translate a session
1. Open a session from [Session History](session-history.md) or after ending a live session
2. Click **Translate**
3. Select the target language
4. Linnet sends the transcript text (not audio) to DeepL and returns a translated version
The translated transcript is saved alongside the original. Tone labels are preserved and displayed against the translated text.
## Tier differences
| Tier | DeepL key source |
|------|-----------------|
| Free (BYOK) | Your own DeepL API key (set `DEEPL_API_KEY` in `.env`) |
| Paid | CF-managed key, no setup needed |
BYOK = bring your own key. DeepL's free tier covers 500,000 characters per month — enough for most personal use.
## Getting a free DeepL API key
1. Go to [deepl.com](https://www.deepl.com/en/pro/change-plan) and sign up for a free account
2. Navigate to **Account** then **DeepL API** and copy your Authentication Key
3. Open your Linnet `.env` file and add:
```dotenv
DEEPL_API_KEY=your-authentication-key-here
```
4. Restart Linnet
## Supported languages
Translation supports all languages in DeepL's catalog. The target language list in Linnet's UI reflects DeepL's current supported outputs. See [DeepL's supported languages](https://support.deepl.com/hc/en-us/articles/360019925219) for the full list.
## Privacy note
Translation sends transcript text to DeepL's servers. Audio is never transmitted. If your session contains sensitive content, review DeepL's [data privacy policy](https://www.deepl.com/en/privacy/) before using translation.
For fully private operation, skip translation or run a local translation model — self-hosted translation support is on the roadmap.

71
mkdocs.yml Normal file
View file

@ -0,0 +1,71 @@
site_name: Linnet
site_description: Real-time voice transcription with tone annotation — tonal subtext labels for calls, appointments, and difficult conversations.
site_author: Circuit Forge LLC
site_url: https://docs.circuitforge.tech/linnet/
repo_url: https://git.opensourcesolarpunk.com/Circuit-Forge/linnet
repo_name: Circuit-Forge/linnet
theme:
name: material
palette:
- scheme: default
primary: blue grey
accent: teal
toggle:
icon: material/brightness-7
name: Switch to dark mode
- scheme: slate
primary: blue grey
accent: teal
toggle:
icon: material/brightness-4
name: Switch to light mode
features:
- navigation.tabs
- navigation.sections
- navigation.expand
- navigation.top
- search.suggest
- search.highlight
- content.code.copy
markdown_extensions:
- admonition
- attr_list
- pymdownx.details
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.tabbed:
alternate_style: true
- tables
- toc:
permalink: true
nav:
- Home: index.md
- Getting Started:
- Installation: getting-started/installation.md
- Local Model Setup: getting-started/llm-setup.md
- User Guide:
- Live Session: user-guide/live-session.md
- Tone Annotation: user-guide/tone-annotation.md
- Elcor Mode: user-guide/elcor-mode.md
- Session History: user-guide/session-history.md
- Translation: user-guide/translation.md
- Corrections: user-guide/corrections.md
- Meeting Notes Export: user-guide/meeting-notes-export.md
- Reference:
- Tier System: reference/tier-system.md
- Privacy: reference/privacy.md
- All CF Docs: https://docs.circuitforge.tech
extra_css:
- stylesheets/theme.css
extra_javascript:
- plausible.js