Skip to content

Concepts

Privacy

Local-first by default. What stays on your machine, what gets mirrored, and how to lock it down further.


aiperson is local-first. Your persona is in a git repo you own. Your corpus is a SQLite file on your laptop. This page describes what actually happens today — not what we intend to build.

What stays local

Your ~/.dotperson/corpus.db stays on your machine. Nothing sweeps it wholesale to a server.

Two things do leave, and both are things you switch on:

The relay’s other jobs are auth, GitHub App brokerage and synthesis.

Which corpus events are mirrored

The corpus-sync push (personkit corpus sync, and the daemon’s sync stage) only sends events whose allow_relay_mirror flag is set. That flag is decided by the harvester that wrote the event, at write time:

Written byallow_relay_mirror
Git harvester (commits, repo state)true
Claude plans harvestertrue
Claude sessions harvestertrue
Reading-list harvestertrue
Config-snapshot harvesterfalse — configs can carry partial secrets pre-redaction
Screenshot harvesterfalse
ChatGPT archive import (personkit import chatgpt)false
MCP bridge entriescaller-specified; false unless the caller opts in

So config snapshots, screenshots and anything you import from a ChatGPT archive are never pushed to the relay, while your git, plan, session and reading-list events are.

There is no per-kind override today. The dashboard privacy screen is where that control will live, and it is being wired — until it ships, the table above is the behaviour. If you want a harvester’s events to stay local, disable that harvester.

Where browser captures live

Browser-extension thread_turn observations carry metadata.{role, conversation_id, external_message_id, url}. The relay fans each captured turn into:

Both of those are relay-side. Browser captures are not written to your local messages table today — mirroring them back down to the local corpus is being built. Until it ships, personkit corpus messages shows your editor and harvester activity, and your browser captures are readable through the dashboard.

You can turn capture off per surface in the popup, or pause every surface at once with the master kill switch.

Dignity controls in the browser

On every supported cloud chat surface (21 of them today — ChatGPT, Claude, Gemini, Copilot, Grok, Meta, Perplexity, Mistral, HuggingChat, Poe, Pi, Lmarena, plus Kimi, DeepSeek, Qwen, Doubao, ChatGLM, Wenxin, Yi, Hailuo, SenseChat), four controls give you the driver’s seat:

  1. Master kill switch in the popup — pauses every surface instantly.
  2. Per-conversation skip — the in-page indicator (bottom-right, on every captured surface) is a one-click toggle for that one chat. Flips to ⏸️ until you flip it back.
  3. Regex redactor — user-defined patterns are applied before the turn leaves the page. Invalid regex is flagged in the popup and never reaches the capture path. Stacks with the relay-side secret-redaction patterns documented below.
  4. Per-surface health dots — green (≤5 min since last accepted turn), yellow (≤60 min), red (otherwise or on error). Visible in the popup AND on the dashboard Overview via the relay’s /v1/me/capture-health endpoint.

A 401 from the relay wipes the cached ID token and surfaces a “Sign in again” CTA — capture never re-queues behind a permanently-failing handshake.

Secret redaction

Every harvested payload passes through secret_redact() before any write. Patterns matched:

  1. Quoted env-style: API_KEY="…"
  2. Unquoted env-style: API_KEY=…
  3. Prefix tokens: gh[pousr]_…, sk-…, xox[abprs]-…, github_pat_…
  4. Authorization headers: Authorization: Bearer …
  5. PEM blocks: -----BEGIN […]-----…-----END […]-----

Redaction applies to both event payloads and message contents.

For cloud captures you can add your own patterns in the extension popup’s “Privacy controls” textarea — those apply in-browser, before the turn ever leaves the page. The dashboard’s extra-redaction-patterns field stores what you enter but is not yet read by the harvest path, so treat the popup as the one that takes effect today.

Signed snapshots

The corpus::snapshot API produces signed JSONL exports (entities / relations / events / provenance + manifest signed by your Ed25519 key). The same key signs every .person.json commit. Round-trip verification proves byte-equal recovery.

Portable export

personkit export --format=markdown emits the whole corpus as Obsidian-ready .md files — one per thread, with YAML front-matter. You own these files outright; dotperson keeps no claim on them. Move them, version-control them, leave the product entirely — the data is yours.

Wiping

What dotperson cannot see