TruthID
SDK Reference

Ruby SDK

Full API reference for truthid-sdk on RubyGems.

Full API reference for truthid-sdk on RubyGems. New to TruthID? Start with the Quickstart — this page is the detailed reference for every method and type once you're integrating for real.

Installation

gem install truthid-sdk

Requires Ruby 3.0+.

TruthID::Client

require "truthid"

truthid = TruthID::Client.new  # defaults to network: "base-mainnet"

There's also a factory function, if you prefer it:

truthid = TruthID.new_client  # equivalent to TruthID::Client.new

Constructor

TruthID::Client.new(network: "base-mainnet", rpc_url: nil)

FieldTypeRequiredDescription
network"base-sepolia" or "base-mainnet"No — defaults to "base-mainnet"Which network to read contracts from
rpc_urlString or nilNoCustom RPC endpoint. Defaults to the public Base RPC for the chosen network

Like Python, network has a default here — you only need to pass it to override or to use the testnet. (The TypeScript SDK requires it explicitly, with no default.)

Methods

create_challenge(origin)

Creates a one-time challenge to embed in the QR code shown to the user.

Parameters

NameTypeDescription
originStringYour site's domain, e.g. "yoursite.com"

Returns AuthChallenge

challenge = truthid.create_challenge("yoursite.com")
challenge.nonce      #=> "3f2e1a4b-..."
challenge.issued_at  #=> 1718000000000

Store it, then delete it

Keep the challenge server-side, keyed by nonce, until verify_auth_response runs — then delete it immediately. See Nonce invalidation below.

Building the QR code — the mobile app expects this exact shape (challenge.to_h already produces the right camelCase keys):

{
  "action": "truthid-auth",
  "challenge": { "type": "challenge", "nonce": "...", "issuedAt": 1718000000000, "origin": "yoursite.com" },
  "callbackUrl": "https://yoursite.com/auth/verify"
}

callbackUrl must use https:// — the mobile app refuses to send the signed response to a plain http:// URL.


verify_auth_response(challenge, response, ttl_ms: 30_000)

Verifies the signed response received from the user's phone. Runs six checks in sequence and stops at the first failure:

  1. User approved (not rejected)
  2. Challenge is within TTL (default: 30 seconds)
  3. Nonce matches the original challenge
  4. Cryptographic signature is valid
  5. Device is registered and active on the blockchain
  6. Retrieves the identity ID linked to this device

Parameters

NameTypeDescription
challengeAuthChallengeThe challenge you created
responseAuthResponseThe response received from the phone
ttl_ms: (optional)IntegerMax challenge age in ms. Default: 30_000

Returns VerifyAuthResult

AuthResponse.from_hash builds it straight from the parsed JSON body — unlike the Python SDK, no manual field mapping needed:

data = JSON.parse(request.body.read)  # { "approved" => ..., "nonce" => ..., "signature" => ..., "deviceAddress" => ... }

response = TruthID::AuthResponse.from_hash(data)
result = truthid.verify_auth_response(challenge, response)

if result.valid
  puts "Authenticated! Identity ID: #{result.identity_id}"
else
  puts "Failed: #{result.reason}"
end

Failure reasons

reasonCause
"User rejected the login request"User tapped "Reject" on their phone
"Challenge expired"More than ttl_ms ms have passed since issued_at
"Nonce mismatch"Response nonce doesn't match the challenge
"Invalid signature format"Signature is malformed
"Signature does not match device address"Signature was not made by device_address
"Device is not active or has been revoked"Device was revoked by the identity owner

verify_session(session_hash)

Checks whether a session hash is still valid (not revoked). Call this on subsequent requests after login to confirm the session hasn't been revoked from another device.

Parameters

NameTypeDescription
session_hashStringbytes32 hex string (0x...)

Returns SessionInfo

session = truthid.verify_session(session_hash)
if session.exists && !session.revoked
  # still logged in
end

check_device_status(device_pub_key)

Looks up a device's current status on the blockchain.

Parameters

NameTypeDescription
device_pub_keyStringEthereum address of the device (0x...)

Returns DeviceStatus

status = truthid.check_device_status(device_pub_key)

compute_smart_account_address(ledger_address, index: 0)

Predicts a user's smart account (controller) address via CREATE2 — pure local computation, no RPC call and no server involved. Useful for showing a deposit address, or checking a balance, before the account has ever been used on-chain.

Parameters

NameTypeDescription
ledger_addressStringThe owning hardware wallet / EOA address
index: (optional)IntegerAccount index, for multiple smart accounts per owner. Default: 0

Returns String — the checksummed smart account address.

smart_account = truthid.compute_smart_account_address(ledger_address)

A module-level function is also available for when you don't need a full client instance:

smart_account = TruthID.compute_smart_account_address(ledger_address, network: "base-mainnet")

Types

All of the following are in the TruthID module. AuthChallenge and AuthResponse are plain classes — their attributes follow normal Ruby snake_case (issued_at, device_address), and each has a conversion method (to_h / from_hash) at the boundary where it meets the camelCase JSON the mobile app actually sends and signs. VerifyAuthResult, SessionInfo, and DeviceStatus are Structs, since they never cross the wire.

AuthChallenge

class AuthChallenge
  attr_reader :type, :nonce, :issued_at, :origin

  # to_h => { "type" => ..., "nonce" => ..., "issuedAt" => ..., "origin" => ... }
end

AuthResponse

class AuthResponse
  attr_reader :approved, :nonce, :signature, :device_address, :session_signature

  # self.from_hash(h) reads h["deviceAddress"] into device_address, etc.
  # session_signature: personal_sign over keccak256(nonce) — always sent alongside signature
end

VerifyAuthResult

VerifyAuthResult = Struct.new(:valid, :identity_id, :device_address, :reason, keyword_init: true)

SessionInfo

SessionInfo = Struct.new(:exists, :revoked, :identity_id, :device_pub_key, :created_at, keyword_init: true)

DeviceStatus

DeviceStatus = Struct.new(:exists, :active, :label, :identity_id, :added_at, keyword_init: true)

Security notes

Nonce invalidation

Delete the challenge from your store before calling verify_auth_response, not after — deleting after leaves a race condition where the same signed response can be submitted twice within the TTL window.

# Correct order
pending_challenges.delete(response.nonce)            # delete first
result = truthid.verify_auth_response(challenge, response)  # then verify

TTL

The default is 30 seconds, matching the mobile app's own challenge expiry. Lowering it is fine; raising it above 30 seconds has no effect, since the phone will already refuse an older challenge.

HTTPS only

The phone POSTs the signed response directly to your callback URL — the mobile app refuses non-https:// URLs, and your endpoint still needs a valid TLS certificate for the connection to succeed.

Networks

NetworkChain IDDescription
"base-sepolia"84532Testnet — for development
"base-mainnet"8453Production (default)
# Testnet during development
truthid = TruthID::Client.new(network: "base-sepolia")

# Custom RPC instead of the public endpoint
truthid = TruthID::Client.new(network: "base-mainnet", rpc_url: "https://your-private-rpc.example.com")

Contract addresses for both networks are in Smart contracts.

Session visibility — nothing to do

Completed logins already show up in the user's TruthID mobile/desktop apps and can be individually revoked from there. The phone registers the session on-chain itself (via a UserOperation) before it ever calls your callback URL — there's no separate registration step for you to call.

If you want to double-check a session landed on-chain, derive its hash from the nonce and call verify_session:

session_hash = "0x" + Eth::Util.keccak256(response.nonce).unpack1("H*")
session = truthid.verify_session(session_hash)
if session.exists && !session.revoked
  # confirmed on-chain
end

Next steps

On this page