← All posts
engineeringprivacy

Terminal Ads Privacy: How We Built Consent and Redaction First

Terminal ads privacy is the constraint we started from, not a feature we bolted on. When we decided to explore contextual ads inside Tybre.md's built-in terminal, the first question was not "how do we make money" but "how do we build this so a developer's secrets never leave their machine." This post is an engineering walkthrough of that design: why the system is off by default, how two-stage consent works, and how a local Rust redaction pass masks secrets to ▮▮▮ before a single byte is sent anywhere.

Before anything else, the honest part. This system does not pay you yet. Earnings accrual is planned, not live; cashout is not supported; and the house ads currently shipping pay $0. We are publishing the architecture now, while the money side is still just scaffolding, precisely because we want the privacy design reviewed on its own merits rather than sold as a side hustle.

Off by default, always

The terminal ad system ships disabled. A fresh install shows no ads, sends nothing, and collects nothing. Nothing about the ad pipeline runs until you go into settings and deliberately turn it on. This is the opposite of the industry default, where telemetry is on until you find the switch to kill it.

Off-by-default matters more than any single technical control, because it means the safe state is the state you get without making any decision. A user who never reads this post, never opens settings, and never thinks about ads is, by construction, fully private. Everything below only applies to people who opt in on purpose.

Two-stage consent, not one buried checkbox

Turning the feature on is not a single toggle hidden in a preferences pane. It takes two deliberate stages: an explicit enable action, and then a separate confirmation that spells out exactly what gets collected and what does not. You have to pass through both to activate anything.

We split it in two on purpose. A single click is too easy to make by accident, and consent that can be given by accident is not consent. The second stage exists so that the moment data collection could begin is a moment you clearly chose, with the terms in front of you. Either stage left incomplete means the system stays in its default silent state.

Local redaction in Rust, before anything leaves

The core of the design is where redaction happens: entirely on your machine, in the native Rust layer, before any network call. Nothing is sent raw and cleaned server-side. The masking runs locally first, and only the masked result is ever eligible to leave.

Concretely, the redaction pass scans collected input for things that look like secrets, API keys, tokens, credentials, and similar high-risk strings, and replaces them with ▮▮▮ in place. By the time the pipeline has anything to transmit, the sensitive substrings are already gone, replaced by opaque blocks that carry no recoverable information. Doing this in Rust, at the same layer that owns the PTY, means the redaction is not a JavaScript afterthought that a rendering bug could skip; it sits on the path everything must cross.

This mirrors the local-first philosophy behind the rest of the app. Just as your notes stay on disk, described in our local-first markdown notes piece, the ad system treats your machine as the trust boundary. Redaction on the near side of that boundary is the whole point.

Only submitted input lines, never keystrokes or output

A terminal sees a lot. It would be trivial, and wrong, to capture everything: every keystroke, every command's output, every file that scrolls past. We deliberately collect none of that. The only thing eligible for collection is a command line you actually submitted by pressing Enter.

  • Not keystrokes: partial typing, backspaces, and abandoned commands are never captured. Only the final, Enter-submitted line is considered.
  • Not output: nothing your commands print, no logs, no file contents, no program results ever enter the pipeline.
  • Not passwords typed at prompts: interactive prompts that do not echo are outside the submitted-line path entirely.
  • Even the submitted line is redacted locally first, so an Enter-submitted command carrying a token still has that token masked to ▮▮▮ before anything moves.

Why this shape, and what is still missing

The reason the architecture looks like this is that contextual relevance needs almost nothing. To show an ad relevant to, say, a developer using Claude Code, the system only needs a coarse signal of what tools you run, which a redacted submitted command line supplies. It does not need your keystrokes, your output, or your secrets, so it collects none of them. Minimizing collection was cheaper and safer than collecting broadly and promising to behave.

We are equally clear about what is not built. There is no live earnings ledger paying real money today, no cashout mechanism, and the ads in rotation are house ads that generate $0. If and when the money side becomes real, it will get the same treatment as the privacy side: shipped only when it is honest to describe. For readers here for the workflow rather than the internals, our Claude Code markdown workflow guide is the practical companion to this deep-dive.

Frequently asked questions

Does the terminal ad system make me money right now?

No. Earnings accrual is planned but not live, cashout is not supported, and the current house ads pay $0. We shipped the privacy architecture first and are honest that the payout side does not exist yet.

Is the ad system on by default?

No. It ships fully off. Nothing is collected or sent until you deliberately enable it through a two-stage consent flow, so a user who never touches the setting stays completely private.

What exactly gets collected if I opt in?

Only command lines you submit by pressing Enter, and only after local Rust redaction masks secrets to blocks. Keystrokes, command output, file contents, and passwords typed at prompts are never collected.

Where does secret redaction happen?

On your machine, in the native Rust layer, before any network request. Secrets are replaced with opaque blocks locally, so raw sensitive strings never leave the device in the first place.

30-second install

Install now

One line in your terminal — no email, no card.

$ curl -fsSL https://tybre.md/install.sh | bash

Prefer a file? Download the DMG