Docs · v0.3.0

SealBox reference

Every command, every option and every setting. If you're just starting, read Learn first.

Installation

Requires Python 3.12 or newer. Runs on Windows, macOS and Linux.

$ pip install sealbox            # or
$ pipx install sealbox           # isolated environment, available everywhere
$ pip install -e .               # from source (at the repo root)

Dependencies: cryptography, pydantic, shamir-mnemonic, keyring, bcrypt.

General usage

sealbox [--lang tr|en] <command> [options]
-h, --help
Show help. sealbox with no command shows the grouped list.
-V, --version
Show the version.
--lang {tr,en}
Message language. Can also be set with the SEALBOX_LANG environment variable. Default: your computer's language.
help <command>
Details and examples for a command (same as sealbox <command> --help).

Commands come in five groups:

GroupCommands
Getting startedinit, status
Everyday useseal, exec, vault
Teamteam
Recoveryrecovery
Githistory, hook

Mistyped commands get a suggestion: sealbox sael → "Did you mean: seal?". The commands are the same in any IDE's terminal (VS Code, PyCharm, Eclipse…).

sealbox init

changes no files

Checks the project and lists the next steps in order. It's the first command to run in a new project.

sealbox init [-w FOLDER] [-c FILE]
-w, --workspace FOLDER
Project root (default: the current folder).
-c, --config FILE
Path to sealbox.toml.
$ sealbox init                     # check the project in the current folder
$ sealbox init -w C:\my-project    # another folder

sealbox status

changes no files

Shows the state of the vault, the key, the recovery code and the team. Every problem line tells you what to do next. Exits with code 1 if something can't be opened.

sealbox status [-w FOLDER] [-c FILE] [--json]
--json
Machine-readable output for scripts and CI.
$ sealbox status
  ✓ Vault: 4 variables, 2 redacted values
  ✗ The vault key is not on this computer  → sealbox recovery restore
  · No team (single user)                    → sealbox team init --github <name>
  ⚠ Exposed secrets in 2 file(s)             → sealbox seal

sealbox seal

rewrites files

Finds passwords, tokens, API keys and personal data in your files:

  • Moves secrets in code into the vault and writes an environment-variable lookup in their place (Python os.environ, JS/TS process.env, Java System.getenv).
  • Redacts data such as emails and national IDs with a __SECURE_..__ placeholder.
  • Takes .env into the vault and leaves .env.sealed in its place.
sealbox seal [-w FOLDER] [-c FILE] [--check] [--json] [paths ...]
paths
Files or folders (default: the whole project).
--check
Don't write, only look. Exit code 1 if secrets are found.
--json
Print the report as JSON: lines, columns and categories, never values. The editor extension uses this.
-w, --workspace
Project root.
-c, --config
Path to sealbox.toml.
$ sealbox seal --check        # only look: any exposed secrets on disk?
$ sealbox seal                # move every secret in the project into the vault
$ sealbox seal src/config.py  # a single file

Run it from the project root. When run from a subfolder, it stops if there's a .env, .git or vault above, because that .env would be left exposed. If you chose that folder on purpose, pass -w ..

A file whose redaction can't be verified is left alone with a [!] warning, and the rest of the scan carries on. Redaction backups are encrypted with the vault key too.

sealbox exec

asks for approval

Runs your program with the secrets from the vault. An approval window first shows which values will be handed over. They're given only to this run, as environment variables. Secrets in the program's output are printed as [REDACTED:..].

sealbox exec [-w FOLDER] -- <command> [arguments...]
-- command
The command to run, after --.
-w, --workspace
Project root (default: the nearest parent folder with a vault).
$ sealbox exec -- python app.py        # Python
$ sealbox exec -- npm start            # Node.js
$ sealbox exec -- java Main.java       # Java
$ sealbox exec -- mvn spring-boot:run  # Maven / Gradle

By design: there's deliberately no flag or environment variable that skips approval. The child process's output goes through a pipe, so interactive or colourful terminal programs may behave differently.

sealbox vault

Manages the entries in the vault. Values are never printed to the terminal on their own. Showing and changing them asks for approval. Every subcommand accepts -w, --workspace.

SubcommandUsageWhat it does
listvault listVariable names and redaction placeholders. Shows no values.
revealvault reveal NAMEShows the value in the approval window. NAME can be a variable or a __SECURE_..__ placeholder.
setvault set NAMEChanges or adds a value (asked for in a window).
exposevault expose PLACEHOLDER [--as NAME]Hands redacted data to the program at runtime. The placeholder becomes os.environ[...] in the code. --as sets the variable name (default: taken from the assignment).
removevault remove [NAME...] [--unused] [--force]Deletes entries (asks for approval). --unused: entries no file uses. --force: delete even if a file still uses it.
resetvault resetIf the key is lost and there's no recovery, creates a new empty vault. The old one is set aside.
$ sealbox vault list
$ sealbox vault reveal DB_PASSWORD
$ sealbox vault reveal __SECURE_TCKN_103d…__
$ sealbox vault set DB_PASSWORD
$ sealbox vault expose __SECURE_EMAIL_8f2c…__ --as ADMIN_EMAIL
$ sealbox vault remove --unused

The same value always maps to the same entry, so sealing again creates no duplicates. An entry whose placeholder or variable name still appears in a file isn't deleted without --force.

sealbox team

Shares the vault with your teammates. The vault goes into the repo, and only the SSH keys of the GitHub accounts on the team can open the values. commit + push after every change.

SubcommandUsageWhat it does
initteam init --github NAMECreates the team with you as owner. This computer's SSH key must be on that GitHub account.
addteam add USER [--owner]Fetches public keys from github.com/USER.keys and grants access. --owner: add as owner.
removeteam remove USERRemoves them from the team. The vault key (DEK) is rotated and the values they may have seen are listed.
promoteteam promote USERMakes a member an owner.
listteam listMembers, roles and SSH fingerprints.
$ sealbox team init --github my-name
$ sealbox team add teammate
$ sealbox team add teammate --owner
$ sealbox team remove teammate
$ sealbox team list
  • Owner / member: owners can add and remove people. The rule is enforced in software: a member who can open the vault could technically hand a copy to someone else.
  • Supported keys: ed25519 (converted to X25519) and RSA (OAEP).
  • SSH keys without a passphrase are rejected. To add one: ssh-keygen -p -f ~/.ssh/id_ed25519. The passphrase is asked for in a window, not in the terminal.
  • Integrity: every entry in team.json carries a MAC made with a key derived from the DEK. Someone who can write to the repo but doesn't know the DEK can't add a fake member or role.

sealbox recovery

A recovery code that reopens the vault if the vault key is lost (Windows reset, new computer). The code and shares are only shown in a window, never printed to the terminal.

SubcommandUsageWhat it does
setuprecovery setup [--new]Creates a recovery code (one per user). --new: replace the existing code with a new one.
sharesrecovery sharesSplits the code into 2-of-3 Shamir shares (SLIP-39). Any 2 are enough.
restorerecovery restore [--kit FILE]Brings the key back with the code or 2 shares. --kit: from a kit, if the vault folder is gone too.
exportrecovery export FILEWrites a recovery kit (locked DEK + encrypted vault). Useless without the code.
statusrecovery status [--json]Is recovery set up?
$ sealbox recovery setup
$ sealbox recovery shares
$ sealbox recovery export D:\project.sapkit
$ sealbox recovery restore
$ sealbox recovery restore --kit D:\project.sapkit

Recovery copies live under ~/.sealbox/recovery/, not in the project folder, so they never enter the repo. Because the public half is stored, new projects are locked to recovery without asking for the code.

sealbox history

changes no files

Searches the whole git history for vault values. A secret committed before it was sealed can still be read in old commits. Values are never printed: only the name, the commit and the file. Exit code 1 if anything is found.

sealbox history [-w FOLDER] [--json]
-w, --workspace
Project root.
--json
Print the report as JSON (used by the editor extension). Contains no values.
$ sealbox history
Git history:
  ⚠ DB_PASSWORD: exposed in 2 commit(s), pushed to a remote
      first: 3f9c2e1 2026-10-02, src/config.py
      → rotate it at the provider, then: sealbox vault set DB_PASSWORD

Pushed means shared: if a commit is already on a remote branch, the value may be in someone else's hands. Rotate it at the provider; rewriting history alone isn't enough.

sealbox hook

Installs a git pre-commit hook: if a vault value is about to be committed, the commit is stopped with file:line. It's a seat belt against accidents; git commit --no-verify skips it. The hook isn't committed, so each teammate installs it on their own machine.

SubcommandWhat it does
installInstall the pre-commit hook
uninstallRemove the SealBox hook
status [--json]Is the hook installed?
runThe check the hook runs (can be run by hand)
$ sealbox hook install
$ sealbox hook status

What's found, what's done

What was foundWhat's done
Password / token / API key as a whole string literal (.py, .js, .ts, .java…)Moved to the vault. Becomes os.environ["DB_PASSWORD"] / process.env.DB_PASSWORD / System.getenv("DB_PASSWORD")
The same value repeated in a commentRedacted with a placeholder
Email, national ID (TCKN), phone, IBAN, nameRedacted with a placeholder. If the program uses it, vault expose turns it into an environment variable
.envSecret lines move to the vault. .env is deleted and a .env.sealed with only placeholders stays
JSON / YAML / TOML config fileRedacted. The report says which environment variable the app should read the value from
Part of a larger string (e.g. inside a URL), or an unsupported languageLeft alone with a [!] warning. Move it by hand
A file whose redaction can't be verifiedLeft alone with a [!] warning. The rest of the scan carries on

Validators

Matches are validated to cut false alarms: the Turkish ID number checksum, email, phone, Luhn (cards), IBAN (mod-97) and entropy. Placeholders look like __SECURE_<CATEGORY>_<128 bits>__.

Key hierarchy

If the vault key (DEK) lived in a single place, losing that place would lock the vault forever. So the DEK has several locks (envelope encryption):

Recovery code (128 bits) ─Argon2id (64 MiB, t=3, p=4)─► Recovery KEK = X25519 ─┐
GitHub SSH key (ed25519→X25519 or RSA-OAEP) ────────────────────────────────────┼─► DEK ─► vault
Windows credential store (cache) ───────────────────────────────────────────────┘
  • Vault encryption: AES-256-GCM, random nonce, replay protection, AAD.
  • Where the DEK lives: in the OS credential store (keyring). On Windows in Credential Manager under the name sealbox, on macOS in the Keychain, on Linux in the Secret Service. Never on disk.
  • Unlock order: Windows store → (with a team) SSH key → an error with a recovery suggestion.
  • If the key was rotated: when another owner rotated it (dek_id mismatch), the new one is fetched via SSH.

What goes in the repo

FileIn the repoDescription
.sealbox/project.vaultyesThe encrypted vault
.sealbox/project.namesyesVariable names and categories (no values). vault list reads this without opening the vault
.sealbox/project.maskedyesPlaceholders of unnamed redacted entries
.sealbox/team.jsonyesMembers, roles, per-person locked copies of the DEK
Backups, lock filesnoKept out by .sealbox/.gitignore
~/.sealbox/recovery/noIn your user folder, outside the project

Watch out: if the root .gitignore excludes the whole .sealbox/ folder, the team commands and status warn you. Secrets committed before the vault was added stay in git history and must be changed.

Configuration: sealbox.toml

Copy sealbox.example.toml to the project root as sealbox.toml, or pass it with --config. Never put real secrets in this file.

sealbox.toml
max_file_bytes = 524288

# To explicitly override the default excludes (.env, *.pem, .git/** ...):
explicit_includes = [".env.example"]

[scanner]
# Names can't be found reliably with regex: list the names to redact explicitly.
person_names = []
# Exact values, to suppress false positives:
allow_values = []
disabled_rules = []

[[scanner.custom_rules]]
id = "project-code"
category = "CUSTOM"
pattern = 'PRJ-\d{6}'
file_globs = ["*.py", "*.md"]
FieldDefaultDescription
excludes.env, *.pem, *.key, id_rsa*, .git/**, .venv/**, node_modules/**, build/**, .ssh/**…Sensitive paths. Never scanned or written (.env is handled separately)
explicit_includes[]To override an exclude on purpose
max_file_bytes512 KiBLarger files are skipped
scanner.person_names[]Names to redact
scanner.custom_rulesAdd rules (id, category, pattern, file_globs)
scanner.disabled_rules[]Turn rules off
scanner.allow_values[]Suppress false positives (exact value)

Settings left over from older versions — [provider], [policy], share_dirs, audit_log_file and max_files_per_job — are ignored.

VS Code extension

The extension is the sealbox command's remote control in your editor; the command does all the work. Install it from the Marketplace or Open VSX or:

$ code --install-extension sealbox-0.3.0.vsix

Settings

SettingDefaultDescription
sealbox.executable""Path to the sealbox program. If empty, the project's .venv is searched first, then PATH. The old secureProxy.executable setting is also read.
sealbox.checkOnSavetrueCheck for exposed secrets when a file is opened or saved.

Features

FeatureWhat it does
Exposed-secret warningsseal --check runs when a file is opened or saved, and secrets get a yellow squiggle
Quick fixLightbulb → "Move this file's secrets into the vault"
Hover__SECURE_..__, os.environ["X"], process.env.X, System.getenv("X") → "Show value" (with approval)
Give at runtimeLightbulb or hover on redacted data → vault expose
Status barNumber of secrets in the vault and people on the team; a warning when there's no recovery backup
Run with the vaultStarts the command in the terminal with sealbox exec (approval every time)
Plain-run hintIf a program started with ▶ fails with KeyError for a variable in the vault, "Run with the vault" is suggested
Team and recoverySet up a team and add or remove people from the menu. Recovery code, shares, kit, restore. These options are offered automatically when the key is missing

A revealed value only appears in a window that closes. If you choose "Copy", the clipboard is cleared after 30 seconds. Menu titles are in English or Turkish, following VS Code's language.

What it shows and never shows

Shows

File paths, category names and counts, line/column, variable names, placeholders, error types.

Never shows

Secret values (only in the approval window, on request), the vault key, the recovery code and shares (only in a window), foreign exception messages (type name only).

Python logging records also pass through the leak filter.

Known limitations

  • Regex can't find every secret. Unusually formatted passwords, and secrets that are split, concatenated, encoded (\u0040, hex, ROT13) or written in natural language, may be missed. Review the report and add custom_rules if needed.
  • Name detection isn't reliable. Only the person_names list and optional Detector plug-ins (e.g. an NER model) are used.
  • Short values (1–2 characters) can't be protected reliably.
  • Wiping memory can't be guaranteed. The key is held as a bytearray and zeroed when done, but Python may make copies. Swap and crash dumps remain a risk.
  • Threat model: it closes the main scenario, "the agent reads a file and sends it to the cloud". A deliberately malicious program running under the same user account that goes after the credential store directly isn't fully blocked.
  • The output filter doesn't catch secrets printed reversed or in pieces. The approval window is the real protection.
  • A removed team member can open the old vault from git history with the old DEK. team remove lists those values; change them at their source.

Architecture

 file ──► SafeFS ──► Scanner ──► Sealer ──────────► file (os.environ["X"] / __SECURE_..__)
          path/symlink regex +      move to vault /     │
          checks       validators   redact              ▼
                                                .sealbox/project.vault (AES-256-GCM)
                                                key: credential store / SSH / recovery code
 exec ──► approval window ──► secrets as environment variables ──► program (output filtered)
sealbox/
├── __main__.py             CLI: commands, help texts and examples
├── cli_help.py             Help plumbing, "did you mean"
├── i18n.py, i18n_en.py     Turkish / English
├── status.py               init / status checklist
├── config.py               Pydantic configuration + safe defaults
├── vaultstore.py           Project vault and key unlocking
├── runner.py               exec, vault reveal/set/expose/remove
├── team.py                 Team sharing via GitHub SSH (team.json)
├── recovery.py             Recovery code, 2-of-3 shares, kit
├── approval.py             Approval and secret-value windows
├── sealing/
│   ├── sealer.py           Move-to-vault / redaction plan
│   ├── rewriters.py        Literal → environment variable (Python, JS/TS, Java)
│   └── file_operations.py  SafeFS: path safety, locking, atomic writes
└── core/
    ├── scanner.py          Regex rules + Detector plug-in interface
    ├── validators.py       TCKN, email, phone, Luhn, IBAN, entropy
    ├── tokenization.py     __SECURE_..__ placeholders
    ├── crypto.py           AES-256-GCM
    ├── envelope.py         DEK locks: Argon2id, X25519, SSH
    ├── key_sharing.py      SLIP-39 Shamir sharing
    └── leak_guard.py       Hiding secrets in output and logs

Migrating from the old name (secure-ai-proxy)

You don't need to do anything:

  • Old .secure_proxy/ vault folders and the ~/.secure_proxy/ recovery folder keep working as they are. New projects use .sealbox/.
  • The old secure-ai-proxy entry in Credential Manager moves to the sealbox name the first time the vault is opened, and the old one is deleted.
  • secure_proxy.toml is still read (sealbox.toml takes priority). The secure-ai-proxy command keeps working for a while too.

Development

$ pip install -e ".[dev]"
$ pytest -q
$ ruff check sealbox tests
$ mypy sealbox
$ cd vscode-extension && npm install && npm test

When adding a new message, wrap the text in _("...") and add its English version to sealbox/i18n_en.py. tests/test_i18n.py catches missing translations. CI: Ubuntu + Windows, Python 3.12/3.13.