vcon-laptop
Record laptop activity — screenshots, microphone audio, and webcam video — into spec-compliant vCon objects. Optionally transcribe audio, describe screenshots with AI, and export session notes to an Obsidian vault.
Features
- Screen capture — periodic screenshots via
mss(cross-platform) with macOSscreencapturefallback - Audio recording — continuous microphone capture via
sounddevice - Video recording — webcam capture via OpenCV at configurable FPS (default 1 fps for activity logging)
- Capture budget — stop recording automatically when a size limit is hit (
MAX_CAPTURE_MB) - AI analysis — fully local by default, no API keys required:
- Audio transcription: mlx-whisper (Apple Silicon)
- Screenshot descriptions: Ollama with any vision model (default
gemma3:4b) - Session summary: generated from transcription + descriptions
- Fallback chain: Ollama → OpenAI → Anthropic (when API keys are configured)
- Obsidian export — markdown note with YAML frontmatter,
![[wikilink]]image embeds, transcription, and AI descriptions - Conserver posting — POST vCon JSON to a conserver endpoint with API token auth and ingress routing
- vCon compliant —
extensions,partieswithvalidation,sha512-base64urlcontent hashes,purpose-based attachments
Quick start
git clone https://github.com/vcon-dev/vcon-laptop.git
cd vcon-laptop
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[analysis]"
# Copy and edit config
cp .env.example .env
# Record for 30 seconds (screenshots only)
vcon-laptop --sources screenshot --duration 30
# Record everything for 2 minutes with a 100 MB cap
vcon-laptop --duration 120 --max-mb 100
Configuration
All settings are loaded from environment variables or a .env file. See .env.example for the full reference.
Capture
| Variable | Default | Description |
|---|---|---|
CAPTURE_SOURCES |
screenshot,audio,video |
Comma-separated sources to enable |
SCREENSHOT_INTERVAL |
5 |
Seconds between screenshots |
VIDEO_FPS |
1 |
Webcam frames per second |
MAX_CAPTURE_MB |
0 |
Stop recording at this size (0 = unlimited) |
MAX_SESSION_DURATION |
0 |
Stop after this many seconds (0 = unlimited) |
Analysis
| Variable | Default | Description |
|---|---|---|
OLLAMA_URL |
http://localhost:11434 |
Ollama server for vision + summary |
OLLAMA_MODEL |
gemma3:4b |
Ollama model (must support vision) |
WHISPER_MODEL |
mlx-community/whisper-small-mlx |
HuggingFace model for mlx-whisper |
ANTHROPIC_API_KEY |
Fallback for image description + summary | |
OPENAI_API_KEY |
Fallback for vision + Whisper transcription |
Obsidian
| Variable | Default | Description |
|---|---|---|
OBSIDIAN_VAULT_PATH |
Path to vault root (blank = skip export) | |
OBSIDIAN_FOLDER |
Recordings |
Subfolder for recording notes |
CLI options
vcon-laptop [--sources screenshot,audio,video]
[--duration SECONDS]
[--max-mb MB]
[--output DIRECTORY]
[--env PATH]
[-v|--verbose]
Press Ctrl+C to stop recording. The session is saved, analyzed, and exported on stop.
vCon output
Each session produces a session.vcon.json file:
{
"vcon": "0.0.1",
"uuid": "...",
"created_at": "2026-04-12T17:45:29+00:00",
"parties": [{"name": "anonymous", "validation": "anonymous", "role": "agent"}],
"dialog": [
{"type": "recording", "mediatype": "image/png", "filename": "screen_00000.png", ...},
{"type": "recording", "mediatype": "audio/wav", "duration": 13.36, ...},
{"type": "recording", "mediatype": "video/mp4", "duration": 8.47, ...}
],
"analysis": [
{"type": "transcript", "vendor": "mlx-community", "body": "..."},
{"type": "report", "vendor": "Ollama", "schema": "screenshot-description-v1", "body": "..."},
{"type": "summary", "vendor": "Ollama", "body": "..."}
],
"attachments": [
{"purpose": "tags", "body": "{\"source\": \"laptop_adapter\", ...}", "encoding": "json"}
]
}
Analysis performance
| Provider | Per screenshot | Summary | Total (2 shots) | Cost |
|---|---|---|---|---|
| Ollama gemma3:4b | ~60s | ~100s | ~7 min | Free |
| OpenAI gpt-4o-mini | ~4s | ~2s | ~10s | $ |
| Anthropic Sonnet | ~5s | ~3s | ~13s | $$ |
Tests
pip install -e ".[dev]"
pytest tests/ -v
69 tests covering config, builder, storage, poster, analysis (with mocked APIs), Obsidian export, and session lifecycle.
Architecture
vcon_laptop/
├── main.py CLI entry point
├── config.py Env-based configuration
├── session.py Session lifecycle + size monitoring
├── builder.py Assembles media into vCon JSON
├── analyze.py Transcription + image description + summary
├── obsidian.py Obsidian vault markdown export
├── storage.py Save vCon to disk
├── poster.py POST to conserver
└── capture/
├── screenshot.py Periodic screen capture (mss / screencapture)
├── audio.py Mic recording (sounddevice callback)
└── video.py Webcam recording (OpenCV)
Related projects
- vcon-lib — Python vCon library
- vcon-server — vCon processing server
- vcon-audio-adapter — Audio file adapter
License
MIT
Metadata
Release files for vcon-laptop 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vcon_laptop-0.1.0.tar.gz | 28.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vcon_laptop-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 53.0 kB
Release files / vcon_laptop-0.1.0.tar.gz
| Download URL | vcon_laptop-0.1.0.tar.gz |
|---|---|
| Size | 28.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b5e4189afdf0283d51de1da96d1319c282b51fedc5bdef928b79862543c2ca54
|
|
BLAKE2b-256 checksum How to use checksums |
744f71047b9e45a46e7d36d9e5018d232d7edc0e9ceb15c9356cd52fe5fddfec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|
Release files / vcon_laptop-0.1.0-py3-none-any.whl
| Download URL | vcon_laptop-0.1.0-py3-none-any.whl |
|---|---|
| Size | 24.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fdd7255f8eee18a8378d5511bd70fb4125a4f5d9eabeac98d716291dd452b5bb
|
|
BLAKE2b-256 checksum How to use checksums |
f10e2eb8faee526fc4974b4f4436767002c1b8c0e5966c61e4e875d524373145
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|