Skip to content

Terminal Masking

Ahmet Zeybek edited this page Sep 27, 2026 · 3 revisions

Terminal Masking

The file is masked, the terminal next to it is not. env, printenv, a failed curl echoing its headers, a test runner dumping its config: all of them put real values on the same screen.

camouflage can mask them, and it is off by default.

require('camouflage').setup({
  terminal = {
    enabled = true,       -- opt in
    keys = 'sensitive',   -- 'sensitive' | 'all'
  },
})

Why it is off by default

There is no file behind terminal output, so there is no parser and no key structure to rely on, only the shape of a line. That is a guess, and a guess that stars half of a build log is worse than nothing.

With keys = 'sensitive' a line is covered when its key matches the sensitive key list of the Weak Secret Check:

API_KEY=***********************
DB_PASSWORD=*******
HOME=/Users/demo
make: build=release

With keys = 'all', every KEY=value line is covered, which hides more and is noisier:

API_KEY=***********************
DB_PASSWORD=*******
HOME=***********
make: *************

Note the last line. make: build=release has the shape of an assignment, so the key filter is what keeps build output readable, not the shape.

A comment marker or a shell export in front of the key is read past, so # OLD_API_KEY=... and export GITHUB_TOKEN=... go through the same list as a bare key.

Values that carry their own credentials

DATABASE_URL is not a sensitive key and should not become one, or every API_URL= line in build output starts getting stars. The password is in the value, though, so a value of the shape scheme://user:pass@host is covered whatever its key is called:

DATABASE_URL=**********************************
API_URL=https://api.example.com/v1

Choosing the keys yourself

key_patterns replaces the sensitive list entirely. They are Lua patterns, matched against the lower-cased key:

require('camouflage').setup({
  terminal = {
    enabled = true,
    key_patterns = { 'password', 'token', 'licence' },
  },
})

Turning it off for one terminal

A TUI that repaints constantly (lazygit, k9s) has no assignments worth covering and plenty of text that looks like one:

vim.b.camouflage_terminal = false

Set it from an autocmd if a particular command always opens the same way:

vim.api.nvim_create_autocmd('TermOpen', {
  pattern = '*lazygit*',
  callback = function(args)
    vim.b[args.buf].camouflage_terminal = false
  end,
})

What it costs

Masks are produced while the screen is drawn, for the rows on screen only, so scrollback and a command printing thousands of lines cost the same as a short one.

Limits

  • only lines that look like KEY=value, key: value or ENV KEY value are considered, with an optional comment marker or export in front
  • a value printed on its own line, or wrapped across lines, has nothing to match
  • the terminal's own text is untouched: copying from it still copies the real output

See Also

Clone this wiki locally