TruthID

Security Model

What TruthID protects against, what it doesn't, and the current audit status.

TruthID's security rests on cryptography enforced by smart contracts and on keys that never leave the user's device — not on trusting a server, because there isn't one in the login path. This page is a candid threat model: what TruthID protects against, what it explicitly does not, and the current audit status. Read it before deciding how much you rely on TruthID for a given use case.

What TruthID protects against

ThreatMechanism
Replay attacks (a captured signed response reused later)30-second challenge TTL + one-time nonce tracking, enforced both by the mobile app and by verifyAuthResponse in every SDK
Front-running a device registration (an attacker submits someone else's device key first)DeviceRegistry uses a commit-reveal scheme — the commitment is bound to msg.sender, so only whoever committed can reveal
Takeover of RecoveryManager during initializationsetRecoveryManager is onlyOwner — found and fixed in the manual security review before the mainnet deploy
Phishing during login approval (a fake site requesting a login)The mobile approval screen shows the requesting origin before the user can approve — a spoofed site shows a different, visibly wrong domain
Interception of the signed response in transitThe mobile app refuses any callbackUrl that isn't https:// — there's no way to point a QR code at a plaintext endpoint
Device key extractionKeys are generated on-device and encrypted at rest by the OS credential store — Android Keystore-backed storage, iOS Keychain, the OS credential manager on desktop (macOS Keychain, Windows Credential Manager, Linux Secret Service). They never leave the device and are never sent to any server, including TruthID's SDKs. (Not the iOS Secure Enclave specifically — Secure Enclave only supports the P-256 curve, and TruthID's device keys are secp256k1, the same curve Ethereum uses. Software-encrypted-at-rest is still a real protection against, e.g., another app or a stolen unencrypted backup reading the key — it's just not hardware-isolated the way a Secure Enclave key would be.)
Forged session creationcreateSession requires an ECDSA signature from the device key plus a cross-check against DeviceRegistry — fixed during the manual security review, which found the function originally permissionless
A lost controller wallet, if guardians were configured in advanceM-of-N social recovery (any threshold up to 20 guardians — there's no contract-enforced default; 3-of-5 is a common recommendation) with a 7-day timelock before it takes effect
A stale guardian set being reused against a new controller after recoveryexecuteRecovery clears the old guardian configuration automatically — the new controller must opt back in
Gas-griefing via an unbounded guardian listRecoveryManager caps guardians at 20
Visual username spoofing (homoglyphs)Usernames are restricted to ASCII letters, digits, -, and .

What TruthID does not protect against

No guardians configured means no recovery, ever

proposeRecovery reverts with GuardiansNotConfigured unless the identity's controller explicitly set up guardians beforehand. There is no other recovery path — losing the controller wallet with no guardians configured means the identity is permanently inaccessible. This is a deliberate trade-off (no backdoor, no admin override), but it means recovery is opt-in, not automatic.

  • A compromised, unlocked device. If malware or an attacker has control of an already-unlocked phone or desktop, they can approve logins the same way the legitimate user would. This is the same limitation every device-based 2FA scheme has — TruthID protects the key in storage, not the device while it's in use.
  • An untrusted RPC endpoint. Every SDK reads device and session state (isDeviceActive, session hashes) from an RPC URL — a public one by default, or one the integrator configures. Nothing on the client cryptographically proves that RPC is telling the truth; a malicious or compromised RPC could falsely report a revoked device as active. Integrators with a high-value login flow should run their own node or pick an RPC provider they trust.
  • Third-party professional audit. The contracts have only had an internal manual review (see Audit status below) — no external firm has audited this code. Treat it as early-stage software.
  • Bugs found after deployment. None of TruthID's contracts sit behind an upgrade proxy, by design — a bug can't be hot-patched in the deployed bytecode. What can change is which deployment an app points at: TruthIDAccount and TruthIDAccountFactory both have an owner-controlled updateRegistries function specifically so an existing smart account can be redirected after the project migrates to a fresh set of contracts, which has happened more than once as bugs like the reentrancy issue above were found and fixed. So "immutable" here means no silent upgrade, not that the deployed addresses are permanent forever — see Smart Contracts for the current addresses and How TruthID Works for how a migration actually happens.
  • The integrator's own backend. Rate-limiting challenge creation, securing secrets, and general server hardening are the integrator's responsibility — TruthID's SDKs verify a signature and a chain read, nothing more.
  • A user being talked into approving a real request. The phone shows the true origin of whoever is asking, but the decision to tap "Approve" is still a human one. Someone tricked by a convincing pretext (or simply not reading the screen) can still approve a login they shouldn't have — the same residual risk every approval-based 2FA carries.

Scope: this page is about logging into TruthID, not the Vault

TruthID Vault (the optional password-manager module in the desktop and mobile apps) can generate TOTP codes and act as a WebAuthn/passkey authenticator — but those are for logging into other services whose credentials you've saved in the Vault, not for TruthID's own login. TruthID itself never asks for a password, a TOTP code, or a passkey — device-key possession plus the approval flow described above is the entire login mechanism. See TruthID Vault for the Vault's own threat model (vault-key derivation, backup encryption, per-device permissions).

Desktop: where secrets live

The desktop app keeps its private keys (device key, vault key, local wallet, Arweave wallet) in the operating system keyring: Credential Manager on Windows, Secret Service on Linux. On Windows this is protected by DPAPI, which ties the secret to the signed-in user account. It is not stored in the TPM — earlier README text saying so was inaccurate.

If the OS keyring is unavailable (a locked-down or headless session, a broken Secret Service daemon), the app falls back to plain, unencrypted files under ~/.truthid (%USERPROFILE%\.truthid on Windows: device.key, vault.key, local_wallet.key, arweave_wallet.json). On Linux those files are created with mode 0600; on Windows the app does not set an ACL, so they inherit the permissions of the folder they are in. Anyone who can read that folder can read them.

The app no longer does this silently: when any of those files exist, the Dashboard and Settings show a warning. Fix the keyring and restart the app to stop relying on the fallback. The optional app lock (Settings) adds a password prompt to opening the app, but does not encrypt the fallback files.

Desktop: the local signer service

While running, the desktop app listens on 127.0.0.1 (loopback only — never a network interface) so the browser extension and third-party apps can ask it to sign, pin, edit the vault or autofill. Two properties matter:

  • Every operation that signs, changes the vault, publishes content or reveals a stored secret is parked until you decide in the app. sign-request, sign-message, pin, vault-edit, autofill-address and autofill-creditcard each open an approval dialog; without your decision the HTTP call times out and nothing happens. An approved autofill can only return one of the candidates the dialog offered. vault-edit additionally requires the requesting device to hold write permission in the vault. An HTTP request alone can never complete any of these.
  • Cross-origin access is open on purpose. The service answers any browser origin (permissive CORS) because the extension's content script calls it from the page's origin. The endpoints have no separate authentication, so any local process — or any web page, if it can find the port — can ask. What it can get is bounded by the approval dialog above. Two things it can learn without approval: that the service is running (ping) and whether the vault holds any saved cards or addresses (the "none saved" answer).

The desktop webview runs with a Content Security Policy, and file access from the UI is limited to files you pick in a native open/save dialog.

Audit status

The contracts have gone through two rounds of manual security review (no automated tooling like Slither or Mythril): access control, reentrancy, front-running, timestamp dependence, DoS, and input validation.

  • Before the mainnet launch, an initial review found 7 issues, including one critical bug (unprotected setRecoveryManager — the same class of bug behind the 2017 Parity multisig hack). All 7 were fixed before deploying.
  • During later development, a follow-up review of contracts/ found a critical reentrancy bug in RecoveryManager.executeRecovery: an attacker who'd compromised the old controller could re-enter proposeRecovery while emergencyWithdraw was moving funds out, and hijack the identity mid-recovery. Fixed by reordering executeRecovery to Checks-Effects-Interactions — state is finalized before any external call is made, rather than relying on a ReentrancyGuard. This is the same fix pattern the original setRecoveryManager finding used.

Reviews like this happen periodically throughout development, not just once before launch — the project's own contribution guidelines call for a review pass per major area (contracts, desktop, mobile, SDKs) before any release. This project has not undergone a third-party professional audit.

If you find a vulnerability, please report it privately via the repository's GitHub Security tab rather than opening a public issue.

Next steps

On this page