External Capture & Import
obsidian-tc can pull content in from outside your vault — passively-captured screen activity, or highlights from a read-later service — through the same staged-and-reviewed pipeline every capture producer uses: nothing lands in your vault until you explicitly approve it.
The pipeline
Section titled “The pipeline”backend (Pensieve, Readwise, …) │ ▼ obsidian-tc CLI import command (import-ambient / import-highlights) │ redact secret-shaped substrings, drop exact duplicates ▼ capture_queue (SQLite — not the vault) │ poison-scanned at enqueue time ▼ list_capture_queue (you, or your MCP client, review the queue) │ ▼ commit_capture (explicit, per-item — the only vault write) │ ▼ your vaultcommit_capture is the only path from the capture queue to a vault note, and it
is always a deliberate, per-item call naming a target path. Every row is scanned
for poison-shaped content the moment it’s enqueued (instruction overrides,
persistence directives, hidden/obfuscated text) and carries that verdict —
none / suspect / high — into the queue for review. high is refused
outright at commit_capture time, even if you try to commit it — it never
reaches the vault regardless of review.
This guide covers both importers obsidian-tc ships today: ambient screen capture (via Pensieve) and Readwise highlight import. Ambient capture is the newer of the two and needs a companion app installed and configured, so it gets most of this page.
Ambient capture (Pensieve)
Section titled “Ambient capture (Pensieve)”What it is
Section titled “What it is”Pensieve (Apache-2.0, formerly “Memos”) is a
privacy-focused passive screen recorder maintained outside obsidian-tc: it
screenshots your desktop on an interval, runs local OCR over each screenshot, and
serves the result through a local HTTP API and web UI. obsidian-tc’s import-ambient
command polls that API, maps whatever it finds into a source-agnostic observation
shape, and stages it for review — it does no screen capture of its own.
That source-agnostic shape (CanonicalAmbientObservation) is deliberately not tied
to Pensieve’s vocabulary — see Extending: writing your own backend
below.
Installing Pensieve
Section titled “Installing Pensieve”Pensieve currently supports macOS and Windows; Linux support is upstream work in progress (documented as such by the Pensieve project itself). If you’re on Linux, keep an eye on the upstream project rather than expecting this today.
pip install memosThen initialize and start it:
memos init # writes ~/.memos/config.yaml and its SQLite databasememos enable # set the service to start on bootmemos start # begin recording screens, serve the web UIBy default Pensieve serves on http://127.0.0.1:8839 and binds to localhost only.
macOS: the first memos start triggers a screen-recording permission prompt —
allow it. memos doctor reports which interpreter path needs the permission
without triggering a fresh prompt; if you upgrade Python (Homebrew, a new pyenv
version, etc.) the grant goes stale and needs re-authorizing via System
Settings → Privacy & Security → Screen & System Audio Recording. Installing
with pipx install memos or uv tool install memos (rather than a bare
pip install) pins the interpreter path so upgrades don’t invalidate the grant.
Windows: prefer an isolated install over a bare pip install:
# uvpowershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"uv tool install memos
# or pipxpip install --user pipxpython -m pipx ensurepathpipx install memosAvoid running it under WSL (WSL cannot capture the Windows host’s screen) or under a Python installed from the Microsoft Store (its sandboxed filesystem breaks Pensieve’s paths).
Pointing obsidian-tc at it
Section titled “Pointing obsidian-tc at it”Set pensieve.baseUrl in your obsidian-tc config to Pensieve’s address:
{ "vaults": [{ "id": "primary", "path": "/home/user/vaults/primary" }], "pensieve": { "baseUrl": "http://127.0.0.1:8839" }}Leaving pensieve out of the config entirely is the supported “not using this
feature” state — import-ambient then no-ops with no network call.
Running Pensieve on a different machine
Section titled “Running Pensieve on a different machine”Pensieve’s default bind (127.0.0.1) only accepts connections from the same
machine it runs on. If obsidian-tc runs on a different box than the one being
recorded — the common setup, since you point ambient capture at whichever machine
you actually work on — reach Pensieve over a private network you control (a
Tailscale/WireGuard-style overlay, or a LAN you trust), not the open internet:
- On the Pensieve machine, edit
~/.memos/config.yamland changeserver_host: 127.0.0.1to the address you want it reachable on (its Tailscale IP, or0.0.0.0to bind every interface — only do this on a network you trust, see below), thenmemos stop && memos start. - Point
pensieve.baseUrlat that address, e.g."http://100.x.x.x:8839".
--machine labeling
Section titled “--machine labeling”Every observation is stamped with a machine label, since a Pensieve instance
polled over the network has no way to identify itself. It defaults to the
hostname in pensieve.baseUrl, but you can override it — useful if you run
Pensieve on more than one computer and want to tell them apart in the queue:
obsidian-tc import-ambient --vault primary --machine laptopobsidian-tc import-ambient --vault primary --machine desktop(Point pensieve.baseUrl at the right instance before each run if you’re
polling more than one machine from the same obsidian-tc config.)
Running it
Section titled “Running it”obsidian-tc import-ambient [path] --vault <id> [--since <iso-date>] [--machine <label>] [--dry-run]- One-shot:
obsidian-tc import-ambient --vault primarypulls the most recent observations (bounded per-run, newest first) and stages them. - Incremental:
--since <ISO 8601 timestamp>scopes to observations captured after that instant — this is how you’d run it on a schedule without re-fetching everything each time. - Preview:
--dry-runreports what would be enqueued (including how many secrets would be redacted) without writing to the capture queue.
obsidian-tc import-ambient --vault primary --since 2026-08-17T00:00:00Z --dry-runScheduling
Section titled “Scheduling”Run it on an interval so ambient capture stays incremental. Track “last run”
yourself (a small wrapper script, or your scheduler’s own last-success time) and
pass it as --since.
cron (Linux/macOS), every 15 minutes:
*/15 * * * * /usr/local/bin/obsidian-tc import-ambient /home/user/.config/obsidian-tc/config.json --vault primary --since "$(date -u -d '20 minutes ago' +%Y-%m-%dT%H:%M:%SZ)" >> /home/user/.local/state/obsidian-tc/import-ambient.log 2>&1launchd (macOS), a user LaunchAgent (~/Library/LaunchAgents/io.obsidian-tc.import-ambient.plist)
running the same command every 900 seconds via StartInterval, invoking
obsidian-tc import-ambient <config> --vault primary --since <timestamp> as its
ProgramArguments — same idea as the cron line above, just launchd’s XML shape
instead of a crontab line.
Task Scheduler (Windows), one-liner via schtasks:
schtasks /create /tn "obsidian-tc import-ambient" /sc minute /mo 15 /tr "obsidian-tc import-ambient C:\Users\you\obsidian-tc\config.json --vault primary" /st 00:00(Overlapping --since windows are safe — re-observed screens dedupe out; see
below.)
Reviewing and committing
Section titled “Reviewing and committing”import-ambient only stages rows in the capture queue. To get them into your
vault, review and commit them — either by asking your MCP client to call
list_capture_queue / commit_capture, or however your client surfaces the
capture queue. Nothing is written to the vault by the import command itself.
Highlight import (Readwise)
Section titled “Highlight import (Readwise)”The same staged-and-reviewed pipeline also backs import-highlights, which pulls
highlights from a Readwise account:
obsidian-tc import-highlights [path] --vault <id> [--since <iso-date>] [--dry-run]Configure it with a Readwise access token (readwise.io/access_token):
{ "readwise": { "token": "your-readwise-token" }}Like pensieve, leaving readwise unconfigured means import-highlights no-ops
with no network call. --since scopes to highlights Readwise has updated after
that instant, and --dry-run previews without enqueuing. Highlights dedupe on a
hash of (source, source id, text, location, highlighted-at), so re-running a sync
never re-stages the same highlight twice.
Privacy and security
Section titled “Privacy and security”- Redaction before enqueue. Ambient observations are scanned for credential-shaped substrings (API keys, tokens, private key blocks, connection strings with embedded passwords, and similar patterns) and those are stripped out before the row ever reaches the capture queue — not after, and not only if a reviewer happens to notice.
- Deduplication. A re-polled, unchanged screen (the same app showing the same text) is recognized and skipped rather than re-staged every run.
- Human review is mandatory. Every ambient observation sits in
capture_queueas staged content only. Nothing reaches your vault without an explicitcommit_capturecall naming a target path — there is no “auto-commit” setting. A capture that fails the poison scan at high risk is refused at commit time even if you try. - Nothing is captured unless you set it up. obsidian-tc does no screen
recording itself. Without
pensieve.baseUrlconfigured,import-ambientnever makes a network call; without Pensieve running, there is nothing to import in the first place.
When Pensieve is unreachable
Section titled “When Pensieve is unreachable”If pensieve.baseUrl is set but Pensieve doesn’t respond (not running, wrong
address, network unreachable), import-ambient fails loudly: it prints the HTTP
status or connection error to stderr and exits non-zero. It does not silently
skip the run or partially stage results — fix the connection and re-run.
Extending: writing your own backend adapter
Section titled “Extending: writing your own backend adapter”import-ambient doesn’t hardcode Pensieve’s data model into the capture pipeline.
Everything downstream of fetching — redaction, dedup, staging — works against one
source-agnostic shape, CanonicalAmbientObservation (source, machine, app,
window_title, text, captured_at, url), defined alongside the ingestion
logic in the server package’s capture/ambient-import.ts. Pensieve’s adapter
(capture/pensieve.ts) is just the first thing that produces this shape from a
real backend’s API — a new one needs only a function that fetches from some other
source and maps its results onto the same fields, the way capture/pensieve.ts does for
Pensieve’s /api/search endpoint. This is the extension point if you want
obsidian-tc to pull ambient observations from a different backend: implement the
mapping, wire it into a CLI entry point the same way, and you get redaction,
dedup, poison scanning, and the human review gate for free.