Security
Security you can read.
The vault
A vault is one JSON file. A random 256-bit data encryption key (DEK) seals the account list with XChaCha20-Poly1305 under a fresh 24-byte nonce on every write. The DEK is wrapped once per unlock method and every wrap sits in the file, so one vault can carry a Secure Enclave wrap for the app and a passphrase wrap for the CLI at the same time. Adding or removing a method re-wraps the DEK; the payload is not touched.
Before encryption the account list is canonical JSON: keys in UTF-8 byte order, Go's string escaping with HTML escaping off, no whitespace, integers only. Secrets are raw bytes in that payload; base32 exists only at otpauth import and export. Readers reject duplicate keys, base64url, missing padding, unknown versions and tampered ciphertext.
Vault format, rendered · spec/vault-format.md · go/internal/vault/vault.go · swift/Sources/TesseraCore/Vault.swift
Unlocking
Passphrase: argon2id at 128 MiB, t=3, p=4 with a 16-byte salt derives the wrap key. Go uses golang.org/x/crypto; Swift uses the vendored PHC reference implementation (portable C, no SIMD), and a known-answer test keeps the two equal. Cost parameters read from a file are bounds-checked before the KDF runs.
Secure Enclave, app only: a non-extractable P-256 key created with privateKeyUsage wraps the DEK through HKDF-SHA256 over a self key agreement. Require Touch ID adds biometryCurrentSet, so the wrap stops opening after a fingerprint change. Set a recovery passphrase in Settings and the same file gains an argon2id wrap, which is also how the CLI opens an app-created vault.
Keychain, CLI only and opt in: tess vault remember stores the passphrase in the macOS login keychain after verifying it against the vault; tess vault forget removes it. The entry is protected at the same level as an ssh key on disk. Scripts can set TESSERA_PASSPHRASE instead.
swift/App/Sources/SecureEnclaveWrap.swift · swift/Sources/CArgon2 · go/internal/keychain
What the app is allowed to do
Three entitlements: App Sandbox, user-selected files for the open and save panels, and app-scoped bookmarks so a vault you opened once (for example the CLI's file) reopens without a prompt. There is no network entitlement. On-screen QR scanning uses ScreenCaptureKit and asks macOS for Screen Recording permission at the moment you scan. Auto-launch, when you turn it on, registers a login item.
The privacy manifest declares the required-reason APIs the app uses. The Mac App Store privacy label reads Data Not Collected. The clipboard is marked concealed when the app copies a code, and the window is excluded from screen capture.
Tessera.entitlements · PrivacyInfo.xcprivacy · App Store listing
What the CLI touches
tess reads and writes the vault at $TESSERA_VAULT or ~/.local/share/tessera/vault.json and holds a lock file next to it for the length of a command, so two tess processes cannot interleave writes. The Go module imports golang.org/x/crypto, cobra, the terminal and QR packages; it imports neither net nor net/http.
Commands that print secrets are explicit and say so in their help: tess export --uri, --secret, --qr and tess show --secret, --uri. Colored output turns off when piped or when NO_COLOR is set.
Two implementations, checked against each other
The Go CLI and the Swift app are separate codebases. spec/testvectors.json and spec/canonical_edge.json pin envelopes, wraps, codes and escaping edge cases; the Go tests, the swiftc verifier and the XCTest suite all run against them on every push, including a full decrypt of a Go-written vault from Swift with real argon2id.
.github/workflows/ci.yml · spec/testvectors.json · swift/Tools/verify/main.swift
Out of scope
This website, and attacks that need root or physical access to an unlocked machine. An attacker who can read your unlocked session can read your codes, in Tessera and in every other authenticator.
Reporting
Report privately through GitHub Security Advisories or security@ibrahemid.com. You get an acknowledgment within 48 hours and a fix or a status update within 7 days, and credit in the release notes unless you prefer otherwise. Do not open a public issue for anything exploitable.