tsafe-cli 1.0.22

tsafe CLI — local secret and credential manager (replaces .env files)
Documentation

tsafe — Explicit runtime authority for secrets

tsafe exec --contract <name> -- <command> — named authority contract in .tsafe.yml / .tsafe.json; tsafe audit / tsafe audit --explain for receipts.

Docs: quick-start · golden paths (incl. coding agents §6) · export-and-exec · cli-reference · security

Most teams still do some version of this: keep passwords in .env, copy them from a password manager into a terminal, export a few secrets into the shell, forget what is still live, then hope nothing leaks into the wrong process, shell history, CI log, or git diff.

tsafe is better because it replaces that ambient mess with one explicit flow: secrets stay encrypted at rest, tsafe set prompts securely for values, and tsafe exec gives one process exactly the secrets it is authorized to receive (via a contract or, as a narrow escape hatch, flags — see the reference). Operator visibility: tsafe audit and tsafe audit --explain answer “what happened?”; flag details and JSON in docs/cli-reference.md.

30-second use

If you use .env today, the shortest mental model is this:

tsafe init

tsafe set DATABASE_URL

tsafe exec -- docker compose up

  • tsafe init creates your encrypted local vault
  • tsafe set stores a secret in the vault and securely prompts for the value
  • tsafe exec -- ... runs one command with every non-alias secret in the vault injected into that process. Parent ambient credentials are stripped first; for true "least authority" use a contract or --keys / --ns (below).

Repeatable / team workflows: define a contract in .tsafe.yml and run tsafe exec --contract <name> -- ... (see export-and-exec and quick-start). Pre-flight: tsafe exec --plan / --dry-run for “what would run” before you execute (same reference and cli-reference).

That is the whole product: encrypted at rest, no plaintext .env file, no ambient shell leak, audit receipt written locally.

tsafe is a local-first secret manager for the problem behind most .env workflows: secrets leak through ambient shell state, copied plaintext files, and inherited parent credentials. It keeps secrets encrypted at rest, injects them only into the process you intend to run, and records what happened in an append-only audit log.

Authority model in one line: with an authority contract or --keys / --ns, a process receives exactly the secrets it is authorized to receive — no ambient access, no leaked parent credentials, no silent empty-string injections. Without one of those flags, tsafe exec injects every non-alias secret from the active vault into the child env (parent ambient credentials are still stripped; the dangerous-name guard is still on). See authority contracts for the recommended pattern.

Problems tsafe solves

  • Ambient credential leaks: parent shell credentials like GITHUB_TOKEN, AZURE_CLIENT_SECRET, or VAULT_TOKEN reach child processes you never meant to authorize.
  • Plaintext secret sprawl: .env files, copied values, and shell exports leave secrets on disk, in shell history, and in git accidents.
  • Missing authority review: scripts say "run the app with secrets" without declaring which process should receive which keys.
  • No operator receipts: you cannot answer which secrets were accessed, by which profile, and when, on a developer machine.

If that is the problem, the smallest working loop is still three commands:

tsafe init

tsafe set STRIPE_API_KEY

tsafe exec -- npm start

The bare tsafe exec -- form above injects every non-alias secret in the vault. That is the right onboarding shape (one secret, one command), but for repeatable scripts and CI use a named authority contract (tsafe exec --contract <name> -- …) so the authorisation set is declared, not implicit.

For repeatable workflows, use a named authority contract (tsafe exec --contract <name> -- ...); example shape and pre-flight are in docs/features/export-and-exec.md and docs/quick-start.md (avoid duplicating long YAML here so docs stay the source of truth).

Default Core Profile

This is the current default-on core product story and the safest mental model for operators and release notes:

  • tsafe exec --contract <name> -- <command> — recommended default for scripts and CI; contracts bundle profile, namespace, allowed/required keys, and trust posture into a single reusable, auditable surface
  • tsafe exec -- <any command> — ad-hoc injection; escape-hatch flags (--ns, --keys, --require, --mode) can narrow a contract but are secondary to named policy — see export-and-exec and cli-reference
  • MCP/editor wrappers are a thin consumer pattern over tsafe exec (and sometimes tsafe agent unlock), not a shipped tsafe mcp product surface
  • tsafe agent unlock — local session unlock for repeated commands; sets TSAFE_AGENT_SOCK so inherited tsafe calls skip password prompts for the TTL when the install channel actually includes the companion tsafe-agent binary
  • Encrypted at rest (XChaCha20-Poly1305 + Argon2id) — no plaintext .env files to leak or commit
  • Multi-profile vaults (dev/staging/prod) — profile is a security boundary, not just organization
  • Append-only audit per profile: tsafe audit, tsafe audit --explain, and JSON via --explain --json (see cli-reference); tsafe doctor for health checks
  • Snapshots/restore, plain .env import, secret generation, TOTP/QR, and age-backed team-vault collaboration core
  • Interactive TUI, biometric/keyring unlock, and Azure Key Vault pull (tsafe kv-pull) as the current default-on core provider path
  • tsafe explain exec|export|namespaces|agent|contracts|pull|vault-recovery — in-terminal docs for authority concepts

Gated Non-Core Features

These surfaces are implemented or in active repo scope, but they should be treated as optional / gated non-core rather than part of the default product tier:

  • Additional cloud/provider pulls beyond AKV: AWS Secrets Manager/SSM, GCP Secret Manager, HashiCorp Vault, and 1Password
  • Multi-source pull orchestration, extended password-manager/browser CSV import, OTS / one-time sharing, and git-helper convenience surfaces
  • Browser extension and native-host flows, SSH key-management surfaces, plugins, and OpenTelemetry

Some repo/docs areas also describe planned-only / post-core surfaces such as VS Code, system tray, and hosted/service-style collaboration depth. Those are not part of the default release story and should not be read as active core features.

Drop-in workflow integration: your Dockerfiles, Makefiles, scripts, and CI pipelines stay unchanged. You wrap the invocation with tsafe exec --contract … -- (preferred) or tsafe exec --.


Two-minute quickstart

Install: every supported path (shell installer, GitHub Releases assets, Homebrew tap / Scoop / WinGet / AUR / Nix, MSI or portable ZIP where published, cargo install only when the matching crates.io dependency graph for that release is actually live, local just install) is spelled out in docs/quick-start.md. Artifacts and naming use tsafe-<version>-default-core-full-<target> when a tagged GitHub release channel actually exists for that line; tracked manifests align with packaging/README.md — use those docs for what ships on a given tag; do not assume a package URL or crates.io install path until that release matches it.

For the current frozen core-only release boundary, a truthful cargo install tsafe-cli claim requires crates.io to already contain the exact compiled standalone tsafe graph for the named release shape you are claiming. In practice, default-core-cli and default-core-cli+agent require the standalone CLI graph (tsafe-cli, tsafe-core, tsafe-azure), while default-core-cli+tui and default-core-full also require tsafe-tui. If that standalone graph is not live yet for the release line, use the tagged release archives/packages or a local workspace install instead of advertising cargo install.

A standalone tsafe install is enough for normal exec usage, but agent-backed local-session reuse still requires a separately shipped tsafe-agent install, and browser/native-host workflows require a separately shipped non-core tsafe-nativehost install. cargo install tsafe-cli alone should not be described as covering those companion runtimes or any broader provider stack.

tsafe init

tsafe set DATABASE_URL

tsafe exec -- docker compose up

# Recommended once you add a contract to .tsafe.yml:

# tsafe exec --contract <name> -- docker compose up

That's it. The command you actually care about gets the secrets it needs in its environment. No .env files, no export juggling. The vault is encrypted at rest and access is audit-logged.

tsafe set DATABASE_URL prompts securely for the secret value, so you do not need to put the value on the same command line.

Contracts are the recommended default for scripts, CI, and team-shared commands: an authority ceiling you can narrow per invocation but never widen. Full examples, edge cases, and pre-flight (--plan / --dry-run) live in docs/features/export-and-exec.md and docs/quick-start.md.

Not sure whether to use exec, get, or export? See docs/features/exec-vs-get.md.

Where files live

tsafe now splits durable vault data, config, and local receipts across the platform directories exposed by ProjectDirs. On Linux that means XDG-style defaults instead of one shared app-data root.

Artifact Default location Notes
Vault files data dir /vaults/<profile>.vault Encrypted vault per profile
Audit receipts state dir /audit/<profile>.audit.jsonl Append-only local receipts for vault access and exec activity
Global config config dir /config.json Default profile and persisted exec settings
TUI debug log state dir /tui-debug.log Written only when TSAFE_TUI_DEBUG=1

Examples:

  • macOS: data ~/Library/Application Support/tsafe/..., config ~/Library/Application Support/tsafe/config.json
  • Linux: data ~/.local/share/tsafe/..., config ~/.config/tsafe/config.json, state ~/.local/state/tsafe/...
  • Windows: platform data/config/state defaults resolved by ProjectDirs for tsafe

If TSAFE_VAULT_DIR is set, that directory becomes the vault root, its parent holds config.json, and runtime receipts/state move under <parent>/state/.

See docs/features/storage-paths.md for the full path matrix and override behavior.


Table of Contents


Trust model

tsafe's core design principle is declared authority over ambient access. Every secret injection is intentional, scoped, and auditable — not a side effect of a shell variable that happens to be set.

Default scope (no contract / --keys / --ns): tsafe exec injects every non-alias secret in the active vault into the child env. Parent ambient credentials are still stripped via the strip list and dangerous-name vault entries (LD_PRELOAD, NODE_OPTIONS, …) still abort, but the vault scope is the whole vault until you adopt one of the levers below. Use a contract, --keys, or --ns to narrow.

Concept What it does
Profile A named, independently encrypted vault. A process can only receive secrets from the profile it is explicitly given (--profile or TSAFE_PROFILE). Profiles are a security boundary, not just an organisational one.
Authority contract (--contract) A named policy loaded from the nearest .tsafe.yml / .tsafe.json that bundles profile, namespace, allowed secrets, required secrets, allowed targets, and trust posture into a single reusable, auditable surface. Flags can narrow a contract but cannot widen it. Prefer contracts in scripts and CI over ad-hoc flag re-composition.
Namespace (--ns) Scopes injection to one sub-tree of keys. --ns api means the child process sees only api/* keys, with the prefix stripped. Everything else in the vault is withheld.
Key selection (--keys) Narrows injection to a named subset. Fails fast if any selected key is missing — no silent empty-string injection.
Require (--require) Asserts minimum key presence before the child starts. Useful in CI to catch misconfigured vaults before a deployment begins.
Trust mode (--mode) hardened — minimal inherited env, redacted child output, dangerous-name deny. standard — compatibility preset. custom — persisted per-profile settings.
Strip list tsafe exec removes parent process credentials (TSAFE_PASSWORD, AZURE_CLIENT_SECRET, VAULT_TOKEN, GITHUB_TOKEN, etc.) from the child environment before injecting vault secrets. Parent ambient credentials cannot reach the child.
Audit log Every vault open, secret read, and exec invocation is appended to a per-profile JSONL log. Operators use tsafe audit and tsafe audit --explain for readable receipts; JSON consumers use --explain --json. Forwarding to a SIEM: tsafe audit-export. Details: docs/cli-reference.md.
Session scope The agent holds a session token with a configurable idle TTL. Vault access requires a live session — there is no "always-on" ambient access mode.

For the full trust model and threat analysis, see docs/features/security.md and docs/features/export-and-exec.md.


Architecture

The repo is intentionally split into one compiled standalone tsafe graph plus separate companion and gated non-core surfaces:

  • The compiled standalone tsafe install path is the tsafe-cli graph over tsafe-core. In the frozen core-only line that graph includes tsafe-azure, and it includes tsafe-tui only for the named standalone shapes that ship the TUI.
  • tsafe-agent is a separate companion binary for local-session reuse (tsafe agent unlock). A standalone tsafe install does not imply it.
  • tsafe-nativehost is a separate non-core companion for browser/native-host workflows. It stays outside the core-only baseline unless a broader release channel explicitly ships it.
  • Additional provider surfaces such as AWS and GCP remain gated non-core additions. They are not part of the baseline standalone tsafe claim, whether they ship as separate crates or as feature-gated inlined modules.
compiled standalone tsafe graph
  tsafe-cli
    -> tsafe-core
    -> tsafe-azure   (core provider path in the frozen core-only line)
    -> tsafe-tui     (only when the named standalone shape includes TUI)

separate companion binary
  tsafe-agent        (required for agent-backed session reuse; not implied by tsafe alone)

separate non-core companion
  tsafe-nativehost   (browser/native-host workflow only when that channel ships it)

gated non-core provider surfaces
  AWS / GCP / ...   (only when a broader channel explicitly enables them)

All vault I/O still flows through tsafe-core, but install truth is shape-specific: one cargo install tsafe-cli claim covers only the compiled standalone tsafe graph for the named release shape, not the separate companion binaries or gated non-core provider surfaces.


Repository Structure

tsafe/
├── Cargo.toml                      # Rust workspace root (6 crates)
├── justfile                        # Common local tasks (build, test, docs, release)
├── rust-toolchain.toml             # Pinned stable toolchain
├── crates/
│   ├── tsafe-core/              # Vault engine, crypto, audit, snapshots, env, TOTP, gen
│   ├── tsafe-cli/               # tsafe binary (clap CLI, 30+ subcommands)
│   ├── tsafe-tui/               # Interactive terminal UI (ratatui)
│   ├── tsafe-agent/             # Background unlock/session agent
│   ├── tsafe-azure/             # Azure Key Vault integration
│   └── tsafe-nativehost/        # Browser extension native messaging host
├── contracts/
│   ├── events/                     # Event and CloudEvents schemas
│   ├── integrations/               # External integration contracts
│   ├── snap/                       # One-time secret (OTS) HTTP payload contracts
│   ├── vault/                      # Vault file JSON schema (v1) + example
│   └── errors/                     # Error envelope schema + reason codes
├── docs/                           # Guides, feature deep-dives, and generated man pages
│   ├── README.md                   # Documentation hub
│   ├── man/                        # Generated Unix man pages (`tsafe.1`, etc.)
│   └── features/                   # Detailed feature guides
├── extension/                      # TypeScript browser extension (Manifest V3)
├── packaging/                      # Tracked package-manager manifests (Homebrew, Scoop, WinGet, AUR, Nix)
├── scripts/
│   ├── __main__.py                 # Cross-platform task entry point (`python -m scripts ...`)
│   ├── build/                      # Release, extension, and MSI packaging helpers
│   ├── setup/                      # Local tooling/bootstrap helpers
│   └── proget/                     # ProGet publish helpers
├── terraform/                      # Azure Key Vault + Log Analytics IaC
├── .pipelines/                     # Azure DevOps: quality, build-and-publish, deploy
├── docker/                         # Multi-stage build + dev container
├── docker-compose.yml              # Dev environment with cargo-watch
├── .githooks/                      # Pre-commit (fmt) + pre-push (clippy, test, TF)
├── tests/                          # Integration and contract validation
└── assets/                         # Icons (ICO/PNG), MSI license (RTF)

Security

Property Implementation
Key derivation Argon2id — m=64 MiB, t=3, p=4 (OWASP-aligned; configurable 8–128 MiB)
Cipher XChaCha20-Poly1305 — 256-bit key, 192-bit random nonce per secret
Key verification Encrypted known-plaintext challenge — wrong password fails fast
Memory safety zeroize::ZeroizeOnDrop on all key material; manual wipe in Vault::drop
Vault atomicity Write to .vault.tmp then atomic rename — no partial writes on crash
Audit trail Append-only JSONL receipt per profile (under the state audit/ directory)
Concurrent access Advisory file lock (.vault.lock); stale lock timeout at 10 min
Auto-healing Vault::open() restores from latest snapshot if file is missing or corrupt
Snapshot retention Encrypted copies under snapshots/<profile>/ in the vault data dir; keeps last 10
Browser isolation Extension holds only a short-lived session token; all vault I/O stays in native host
HTTPS enforcement AKV URLs and OTS (TSAFE_OTS_BASE_URL) must use https://

For tracked security findings and their status, see docs/security/findings.md.


Installation

Supported formats and channels vary by release (e.g. Windows MSI / portable ZIP; macOS and Linux .tar.gz on GitHub Releases; optional package managers; cargo install only once the relevant crates are actually published to crates.io for that release line). For the frozen core-only Cargo path, crates.io must contain the exact compiled standalone tsafe graph for the release shape you are claiming: default-core-cli and default-core-cli+agent need tsafe-cli, tsafe-core, and tsafe-azure, while default-core-cli+tui and default-core-full additionally need tsafe-tui. Any +agent claim still needs separate tsafe-agent publish/install truth, and browser/nativehost or broader provider claims need their own non-core channel proof. Canonical install matrix — including shaped tsafe-<version>-default-core-full-<target> release archives when a tagged GitHub release channel actually exists, TSAFE_INSTALL_* overrides, private repos, just install, and companion binaries such as tsafe-agent / tsafe-nativehost when a release claims those workflows — is docs/quick-start.md. Tracked packaging manifests and tap/bootstrap notes: packaging/README.md. Use whatever install method matches the artifacts you actually publish for your tag.


Developer Quick Start

1. Install tooling (once, for development)

just setup

# On Windows, follow any printed install-tooling.ps1 guidance for WiX/MSVC.

2. Install git hooks (once)

git config core.hooksPath .githooks

3. Build and run

cargo build --release --package tsafe-cli

cargo run -p tsafe-cli --bin generate-manpages -- docs/man

# Unix:    ./target/release/tsafe --help

# Windows: .\target\release\tsafe.exe --help

# Repo-local manual: man -l docs/man/tsafe.1

Or with Podman/Docker (no local Rust required):

podman compose up --build

4. Create your first vault

tsafe init                              # create 'default' profile
tsafe set DB_URL "postgres://localhost/mydb"
tsafe set API_KEY --tag env=dev         # prompted securely for value
tsafe list
tsafe exec --dry-run                    # preview env var names only (no subprocess)
tsafe exec -- dotnet run                # inject secrets as env vars

5. Migrate from .env files

tsafe import --from .env.local --overwrite

Broader password-manager/browser CSV imports are a gated lane that require a build or release channel with pm-import-extended:

tsafe import --from bitwarden --file ~/Downloads/export.csv
tsafe import --from chrome --file ~/Downloads/chrome_passwords.csv

For a full walkthrough, see docs/getting-started.md. For the full docs index, see docs/index.md. For Unix manual generation and install notes, see docs/man/README.md.


CLI Reference

tsafe [--profile <name>] <command>

The --profile flag selects a named vault (default: default). Override with the TSAFE_PROFILE env var.

Core Commands

Command Description
init Create a new encrypted vault for the current profile; on a TTY, optionally set up OS quick unlock (Touch ID / Hello / PIN) or defer with biometric enable later
config show, config set-backup-vault … Global config.json: optionally copy each new vault's master password into a hub vault at profile-passwords/<profile>
set <KEY> [VALUE] Store a secret (prompts securely if value omitted). --tag KEY=VALUE to attach metadata, --overwrite to skip confirmation
get <KEY> Print a secret. --copy copies to clipboard and auto-clears after 30 s
delete <KEY> Remove a secret (snapshot taken automatically)
list List all secret keys. --tag env=prod to filter
export Print secrets to stdout. --format env|dotenv|powershell|json|github-actions. --tag to filter
exec -- <cmd> Run a command with secrets as env vars. Propagates exit code and Ctrl-C
import Migrate from .env files by default. Broader CSV exports such as bitwarden, 1password, lastpass, chrome, edge, or firefox require a build/channel with pm-import-extended. --overwrite to replace existing
rotate Re-encrypt all secrets with a new master password

Generation and TOTP

Command Description
gen <KEY> Generate a CSPRNG random secret. --length 64 --charset hex --print
totp add <KEY> <SECRET> Store a TOTP seed (base32 or otpauth:// URI)
totp get <KEY> Print current 6-digit TOTP code + seconds remaining
qr <KEY> Render a secret as a QR code in the terminal

Profiles and Organization

Command Description
profile list List all vault profiles
profile delete <name> Delete a profile vault. --force to skip confirmation
diff Show key-level changes since last snapshot
compare <profile> Highlight missing/mismatched keys between two profiles
pin <KEY> / unpin <KEY> Pin secrets to the top of lists
alias <TARGET> <ALIAS> Create an alias (tsafe get ALIAS resolves to TARGET). --list to view all

External Integrations

Command Description
kv-pull Pull secrets from Azure Key Vault. --prefix MYAPP_ --overwrite
vault-pull Pull from HashiCorp Vault KV v2. --addr, --token, --mount, --prefix
op-pull <ITEM> Pull fields from a 1Password item via the op CLI
share-once <KEY> Share via a configurable OTS one-time link (snap alias). --ttl string is server-specific

Audit and Diagnostics

Command Description
audit Show recent audit log entries. --limit 100
audit-export Export audit log. --format json|splunk --output audit.jsonl
snapshot list List local vault snapshots
snapshot restore Restore the most recent snapshot
doctor Diagnose vault health, env vars, expired secrets, orphaned snapshots

Utilities

Command Description
git <subcommand> [args] Gated non-core git-helper lane: run git with ADO_PAT injected as http.extraHeader
hook-install Install a secret-scanning git pre-commit hook. Auto-detects nearest .git
completions <SHELL> Print shell completions (powershell, bash, zsh, fish)
browser-profile add|list|remove Map browser domains to vault profiles
browser-native-host register|unregister Register or remove the browser native messaging host bridge
ui Launch the interactive terminal UI

For the full reference with examples, see docs/cli-reference.md.


Git Integration

tsafe git is an implemented gated non-core helper lane. When the current binary and release channel actually include it, it wraps any git command and injects vault credentials via http.extraHeader — no URL mutation, no temp files, no $env: cleanup:

# Push to Azure DevOps with ADO_PAT from vault
tsafe -p main git push ado main

# Or use the push script
python scripts/push.py

The agent + master-password bridge means zero prompts in your daily workflow for commands that are actually present in your compiled build. For tsafe git, that still requires the gated git-helper lane:

tsafe agent unlock --ttl 8h    # once; copy the printed TSAFE_AGENT_SOCK line into your shell
tsafe exec -- cargo test       # any time

See docs/features/git-integration.md for the full reference.


Workflows and Automation

tsafe is designed to replace every pattern that involves setting $env:SECRET = ... manually:

For repeated local automation, the feature you want is tsafe agent unlock, not tsafe unlock. tsafe unlock only removes a stale .vault.lock file after a crash. tsafe agent unlock starts the background session agent, prints a TSAFE_AGENT_SOCK export line, and every later tsafe command that inherits that env var can open the vault without re-prompting for the password.

tsafe agent unlock --ttl 8h
# copy the printed line into the parent shell:
#   $env:TSAFE_AGENT_SOCK = "..."

tsafe exec -- cargo test
tsafe exec -- terraform apply
# If the gated git-helper lane is compiled in, this can use the same session:
# tsafe git push ado main

That is the right pattern for local scripts, long-lived dev shells, and parent processes that spawn several tsafe commands. Headless CI should still set TSAFE_PASSWORD from the platform secret store instead of relying on the agent socket.

# Run anything with secrets injected
tsafe exec -- dotnet run
tsafe exec -- cargo test
tsafe exec -- docker-compose up
tsafe exec -- terraform apply

# Replace complex credential setup scripts
tsafe exec -- powershell -File ./deploy.ps1

See docs/features/workflows.md for patterns covering:

  • Session-based unlock (one password, all day)
  • Replacing $env: patterns
  • CI/CD pipelines with minimal secret surface
  • Multi-profile automation

Profiles

Each profile is an independent encrypted vault file under the platform data vaults/ directory (~/.local/share/tsafe/ on typical Linux installs).

tsafe --profile dev  set DB_URL "postgres://dev-host/mydb"
tsafe --profile prod set DB_URL "postgres://prod-host/mydb"
tsafe compare prod       # show what dev has that prod doesn't

Override the vault directory for CI: $env:TSAFE_VAULT_DIR = "C:\ci\vaults"


Observability

tsafe uses the tracing crate for structured logging. Controlled via the TSAFE_LOG environment variable:

# Disabled by default (zero overhead when unset)

tsafe get DB_PASSWORD


# Enable debug logging to stderr

TSAFE_LOG=debug tsafe get DB_PASSWORD


# Info-level only

TSAFE_LOG=info tsafe exec -- dotnet run


# Per-target granularity (tracing env-filter syntax)

TSAFE_LOG=tsafe_core=debug,tsafe_cli=info tsafe rotate


# JSON lines on stderr (aggregators / CI). Uses span-close records for `#[instrument]`d work (vault, KDF, …).

TSAFE_LOG=info TSAFE_LOG_FORMAT=json tsafe list

Key operations are instrumented with timing spans: vault open/save, Argon2id KDF, XChaCha20 encrypt/decrypt, secret set/get, rotation. Auto-heal events (snapshot restore on corruption) are logged at warn level.


Browser Extension

tsafe includes a browser extension (Edge, Chrome, Firefox) that autofills credentials without any cloud relay.

This lane is implemented but gated non-core in the current product model. It is not part of the default-on core story, and it depends on the separate native host/runtime topology described in docs/features/browser-extension.md.

Browser popup / content script
        ↕  Native Messaging (stdio JSON-RPC)
tsafe-nativehost  (registered by `tsafe browser-native-host ...`)
        ↕
`<vaults-dir>/<profile>.vault`

Features (v1 — see docs/features/browser-extension.md for full scope and out-of-v1 items):

  • Autofill on <input type="password"> detection (user-initiated)
  • Domain-to-profile mapping (tsafe browser-profile add github.com)
  • HTTPS-only content script — autofill UI does not run on http: pages; advanced lookalike / homoglyph guards per roadmap, not a v1 bar
  • Context menu: right-click any password field to fill
  • Popup UI: search, copy, reveal, lock

Build:

cd extension && npm install && npm run build
# or
python scripts/build/build_extension.py

Terminal UI (TUI)

Launch with tsafe ui or tsafe --profile prod ui.

Key bindings:

Key Action
Arrow keys Navigate secret list
Enter Unlock vault / confirm action
s Set (add/edit) a secret
Delete Remove selected secret
r Rotate master password
/ Search filter
? Help overlay
Tab Switch profiles
Ctrl-C Quit

Screens: Login, Dashboard, Edit Modal, Audit Log, Snapshot Restore, Help.


External Integrations

Within the current product boundary, Azure Key Vault is the only default-on core provider path here. The other provider bridges below remain implemented but gated non-core and should not be read as part of the default release tier.

Azure Key Vault

$env:TSAFE_AKV_URL = "https://myvault.vault.azure.net"
$env:AZURE_TENANT_ID = "..."
$env:AZURE_CLIENT_ID = "..."
$env:AZURE_CLIENT_SECRET = "..."

tsafe kv-pull --prefix MYAPP_ --overwrite

Falls back to IMDS managed identity when service principal credentials are not set (for Azure VMs / ACI).

HashiCorp Vault

$env:TSAFE_HCP_URL = "http://vault:8200"
$env:VAULT_TOKEN = "hvs.xxx"

tsafe vault-pull --mount secret --prefix myapp/

1Password

tsafe op-pull "Database Credentials" --op-vault Personal

Requires the op CLI installed and authenticated.


Infrastructure

Optional Azure infrastructure for audit log ingestion and cloud-backed secrets.

# Plan
cd terraform
terraform init -backend-config=../envVars/backend-config.azurerm.hcl
terraform plan -var-file=../envVars/<your-tsafe-env>.tfvars

# Apply
terraform apply -var-file=../envVars/<your-tsafe-env>.tfvars

Resources provisioned:

  • Azure Resource Group
  • Azure Key Vault (RBAC, purge protection, soft delete)
  • Log Analytics Workspace (90-day retention)
  • Diagnostic settings routing KV audit events to LAW

Pipelines

Pipeline Trigger Purpose
.pipelines/quality.yaml PR + main push fmt, clippy, test, contract validation, TF validate (Ubuntu)
.pipelines/build-and-publish.yaml Tag push (tsafe-v*) / manual Multi-platform build (Windows/macOS/Linux tickboxes), MSI, ProGet
.pipelines/deploy-infrastructure.yaml Manual Terraform Plan / Apply / Destroy
.github/workflows/release.yml Tag push (tsafe-v*) / manual GitHub Actions: multi-platform build + GitHub Release

Local Development

# Run all quality checks (same as CI)
just ci

# Regenerate Unix man pages from the live clap command tree
just manpages

# Watch and re-run tests on file change (Podman)
podman compose up

# Build a portable release archive for the current platform
python scripts/build/build_release.py --shape default-core-full

# Build browser extension packages
python scripts/build/build_extension.py

# Run secret scanner
python scripts/secret_scan.py

Windows MSI packaging and ProGet publish flows are wired through the build pipelines and helper scripts under scripts/build/ and scripts/proget/.

Git: fork and upstream

If you fork tsafe, add the canonical repo as upstream so you can sync main:

git remote add upstream https://github.com/0ryant/tsafe.git

git fetch upstream

Use SSH URLs if you prefer. Do not embed personal access tokens in remote URLs (rotate any token that was ever committed or copied into a URL). origin is your fork; upstream is the public source you pull from.

Homebrew tap packaging (separate public repo) is documented in packaging/README.md; bootstrap with just homebrew-tap-bootstrap.

Dev container

VS Code / GitHub Codespaces: open the repo in a container using .devcontainer/devcontainer.json (Rust bookworm image, rustfmt + clippy installed on create).

Docs vs code

When you change CLI behavior, env vars, or extension UX, keep README.md, docs/, and .env.example accurate. Cursor picks up .cursor/rules/docs-truth.mdc for touched paths.

Crate versions and pre-push

Workspace crates (tsafe-agent, tsafe-azure, tsafe-aws, tsafe-cli, tsafe-core, tsafe-gcp, tsafe-nativehost, tsafe-tui) each have their own [package].version in crates/<name>/Cargo.toml. Release tarball and extension versioning follow tsafe-cli (see scripts/_util.py). Do not read that list as a claim that those crates are already live on crates.io for the current release line.

Crates.io publish/install truth follows the manifest graph, not just the top-level product tag. In the frozen core-only model, cargo install tsafe-cli is truthful only when the exact compiled standalone tsafe graph for the claimed release shape is live on crates.io: default-core-cli and default-core-cli+agent require tsafe-cli, tsafe-core, and tsafe-azure, while default-core-cli+tui and default-core-full additionally require tsafe-tui. That is enough for the compiled tsafe binary claim only. tsafe-agent and tsafe-nativehost remain separate published/runtime companions rather than transitive installs from tsafe-cli, and broader provider stacks remain outside that Cargo claim.

  • Bump helper: python3 scripts/version_set.py 1.0.2 (all crates to a target), python3 scripts/version_set.py --crate tsafe-core --bump patch, or python3 -m scripts version ….
  • Version policy: see docs/release-versioning.md for when to use patch vs minor (0.6.0, 0.7.0, etc.) while the repo is still pre-1.0.
  • Pre-push: With git config core.hooksPath .githooks (set by just setup), pushes to main require a version bump on every crate whose files changed versus the remote tip (or all six if the workspace root Cargo.toml changed). Override refs with TSAFE_VERSION_HOOK_REFS (comma-separated, fnmatch allowed, e.g. refs/heads/main,refs/heads/release/*).

Docker

# Dev container (cargo-watch, full toolchain)
podman compose up --build

# Production image (~50 MB, non-root)
podman build -f docker/Dockerfile.build -t tsafe .
podman run --rm tsafe --help

MSI Distribution

cargo-wix currently generates the broader gated-broader-dev MSI artifact tsafe-<version>-x86_64.msi that:

  • Installs tsafe.exe and tsafe-nativehost.exe under C:\Program Files\tsafe\ (see crates/tsafe-cli/wix/main.wxs)
  • Ships tsafe-nativehost.exe, but browser/native-host registration is still a manual post-install step: run tsafe browser-native-host register --extension-id <chromium-id> after installing the matching Chromium-family browser extension on Windows
  • Preserves an existing browser/native-host registration across in-place MSI upgrades; initial registration is still a manual post-install step
  • Adds the install directory to PATH
  • Creates Start Menu and desktop shortcuts
  • Registers an uninstaller in Add/Remove Programs
  • Upgrades in-place using a stable GUID

That Windows MSI path is a channel-specific broader install shape, not proof that every default-core tag or cargo install tsafe-cli path includes browser/nativehost coverage. The current default-core tagged release channel remains portable ZIP-only.

Windows portable ZIP names are shape-specific. When a tagged default-core release channel exists, it uses tsafe-<version>-default-core-full-x86_64-pc-windows-msvc.zip; local Windows packaging helpers may also emit tsafe-portable-<shape>-<target>.zip for copy-anywhere deployment without elevation.

The MSI is published to a ProGet Universal feed (e.g. tsafe) via the build-and-publish pipeline.


Contracts

tsafe follows a contracts-first design. Schemas are defined before implementation.


Principles

  • Contracts first — vault schema and error envelope defined before implementation
  • TDD — every module has #[cfg(test)] tests; hooks enforce cargo test before push
  • DRY — shared logic in tsafe-core; CLI is a thin dispatch layer
  • Idempotenttsafe set is safely re-runnable; vault rotation is atomic
  • Shift left — pre-commit (fmt), pre-push (clippy + test + TF validate), CI quality gate
  • Right tool — Rust core (memory safety, zeroize, static binary), Python/Pwsh automation, Terraform IaC
  • Extensibletsafe-core is a standalone crate; new workspace members bolt on cleanly

Documentation

Feature index and docs hub: docs/index.md.

Guides

Document Description
Feature Index Hub for guides, feature docs, and the fastest path to deeper product documentation
Getting Started Prerequisites, install, first vault, CI integration
CLI Reference Full command reference with flags, env vars, and examples
Man Pages How to regenerate, install, and use tsafe.1 and subcommand manuals
Security Findings Tracked security review findings and their status
Internal Pitch Why tsafe + AKV, the PowerShell memory problem, addressing security team concerns
Competitive Analysis Feature and security comparison vs 12 alternatives, gap analysis
Feature Maturity Every feature: current state, MVP path, AAA path
Roadmap Feature tracking — shipped and planned

Feature Deep-Dives

Document Description
Vault Basics Init, set, get, delete, list — with key naming rules and password management
Export and Exec Output formats (env, dotenv, powershell, json, github-actions) and secret injection
Import Migrate from .env files by default; broader Bitwarden, 1Password, LastPass, and browser CSV imports require pm-import-extended
Profiles Multi-profile workflows, compare, and diff
Secret Generation CSPRNG random secrets with configurable length and charset
TOTP Time-based one-time passwords — use tsafe as an authenticator
Audit Logging Append-only JSONL audit trail with Splunk/JSON export
Snapshots Automatic backups, manual restore, and auto-healing
Cloud Integrations Azure Key Vault core path plus gated HashiCorp Vault / 1Password / OTS surfaces
Browser Extension Gated browser/native-host flows — v1 scope and roadmap
Terminal UI Interactive dashboard with keyboard shortcuts and screen reference
Organization Tags, pins, and aliases for managing secrets at scale
Developer Tools Doctor, secret scanning hooks, shell completions, QR codes, SSH keys, rotation policies
Security Encryption model, memory safety, atomic writes, and threat model
Team Vaults Multi-recipient age encryption — shared vaults for teams
Biometric Unlock Touch ID, Windows Hello, Linux Secret Service — password-free vault access

About

tsafe is part of the algol.cc ecosystem — small, composable tools for explicit authority in software systems.