Segundo

Passkeys on Segundo, with Mera

One passkey, many keys.

Your Segundo passkey makes four keys, each with one job, none of them worked out from another.

A passkey never leaves your phone's secure chip. When you confirm with your fingerprint, it can compute 32 bytes from a secret it keeps and a “salt” the site chooses (WebAuthn's PRF extension). Same passkey, same salt: the same bytes, on every device your passkey is synced to. A different salt gives unrelated bytes. Segundo uses four salts, so one passkey stands behind four separate keys, and none of them is ever stored anywhere.

One passkey → four keys

Each key comes from its own PRF salt. Holding one tells you nothing about the others.

Your passkey · one fingerprint, a secret that never leaves your phone
  1. Wallet keypays

    PRF salt
    sha256(“mera.prf.salt.v1”)
    Becomes
    BIP-39 entropy → m/44'/60'/0'/0/0 → secp256k1

    Signs every action on a tab: opening it, clocking in, paying out, closing. The signatures go to Segundo's relayer, which submits them on Monad and pays the gas.

    Why only money uses it. It is the standard Ethereum path, so any BIP-39 wallet could open the account if the phrase were ever exported. Money needs a key every wallet understands; nothing else should share that exposure, so nothing else is made from it.

  2. Sealing keyyour private details

    PRF salt
    sha256(“segundo/seal/v1”)
    Becomes
    HKDF-SHA-256 → AES-256-GCM

    Encrypts your legal name, address, tax ID and payout account in your browser. They print on invoices and income statements only after you confirm with your passkey.

    Why it’s not the wallet key. Your details outlive any one session and any one wallet. A leaked wallet key or a stolen signature opens nothing here, and opening your details never needs the key that moves money.

  3. Invite keysrebuild any invite

    PRF salt
    sha256(“segundo/invite/v1”)
    Becomes
    HKDF-SHA-256 per tab → secp256k1

    One key per tab you open for someone you invite by link. The contract stores its address; the link carries the private half; the freelancer claims the tab with a signature from it. Lose the link and your passkey makes the same key again, on any device.

    Why it’s not the wallet key. Invite keys travel in links you paste into chats and emails. Whoever holds one link holds that one key: it says nothing about your other invites, your details or your wallet.

  4. Disclosure keyshare with a landlord, only them

    PRF salt
    sha256(“segundo/disclose/v1”)
    Becomes
    HKDF-SHA-256 → X25519

    Seals the details you pick for one verifier's browser: a landlord, a bank, an employer. They see them next to your on-chain income, and nobody else can read the reply, Segundo included.

    Why it’s not the wallet key. This key meets keys made by strangers. Keeping it apart means sharing with a verifier never touches the key that holds your money or the one that guards your stored details.

At a glance

What each key is for, when your passkey asks, and where it lives. None of them is ever written to disk or sent to a server.

KeyPRF saltWhen your passkey asksKept
Wallet keymera.prf.salt.v1When you sign in, and after 15 minutes idlePage memory, while you're signed in
Sealing keysegundo/seal/v1Its own prompt, when you open your detailsPage memory, until the session locks
Invite keyssegundo/invite/v1Once a session, when you open or rebuild an invitePage memory; the link itself in this browser
Disclosure keysegundo/disclose/v1Shares the sealing prompt: one fingerprint for bothPage memory, until the session locks

Invite keys

Lose the link, not the invite.

When you invite a freelancer by link, the money waits in the contract until someone claims it with the key in that link. With a random key, a lost browser meant a lost invite. With a passkey account, the key is rebuilt from your passkey and Monad, on any device.

  1. 1

    You open the tab

    Segundo reads how many tabs your account already has on the contract, tabsOf(you).length. That number is the new tab's slot: its place in your list, fixed forever because the list only grows.

  2. 2

    Your passkey makes the key

    The invite key comes from your passkey's invite salt and the slot. Its address goes on Monad with the tab; its private half goes after the # in the link, which never reaches a server.

  3. 3

    Lost the link? Rebuild it

    On any device, sign in with the passkey and open the tab: “Rebuild the invite link” finds the tab's slot on Monad, makes the same key, and checks its address against the one the contract holds before showing the link.

invite key = HKDF-SHA-256(

PRF(passkey, sha256(“segundo/invite/v1”)),

“tab:10143:0xeb6c…13d1:you:slot”)

Chain 10143 is Monad testnet; 0xeb6c…13d1 is the Segundo contract (deployed under Segundo's working name, Till). Its claim function checks the freelancer's signature from this key on chain. If a second tab landed in your list at the same moment, the rebuild tries the slots next to it and keeps only the key whose address matches. Nothing about the invite is stored anywhere but Monad.

Email and practice accounts have no passkey, so their invite keys are random and kept in the browser that made them, as before.

Disclosure key

Show a landlord. Only them.

Segundo's income statement proves what you earned from Monad itself. A landlord or a bank also needs to know it's you. Selective disclosure hands them exactly the sealed details you pick, readable in their browser and nowhere else, with proof they came from the account on the statement.

  1. 1

    They ask

    The verifier opens your statement with ?request=1. Their browser makes a one-time key pair, keeps the private half, and shows a link and QR code for you.

  2. 2

    You choose and confirm

    On your statement, tick what to share: legal name, address, tax ID, payout account. One fingerprint opens your sealed details and makes your disclosure key; the page seals your picks for their key alone and gives you a reply link to send back.

  3. 3

    They check

    Their page checks that your account signed the disclosure key, decrypts the reply, and shows “Verified details for this statement” beside the totals read live from Monad. It prints with the statement.

shared = X25519(your disclosure key, their one-time key)

key = HKDF-SHA-256(shared, “segundo/disclose/v1”, both public keys)

reply = AES-256-GCM(your picks), bound to the statement's address, the Monad block it was read at, and both keys

Your wallet key signs one line, once: “this disclosure key belongs to account 0x…”. The verifier recovers the signer from that signature and checks it is the statement's account. Change one byte of the reply, or open it against another statement or another request, and it doesn't open.

Why separate keys

Who holds what

Segundo's servers never hold

  • Your passkey's secret, or any PRF output
  • Any of the four keys
  • Your sealed details, sealed or open
  • An invite link's key, or a verifier's reply

Your browser keeps

  • Which passkey is yours and your public address
  • Your details as ciphertext
  • Invite links you made, so you don't need to rebuild them
  • The public signature that ties your disclosure key to your account

Monad keeps

  • Each invite key's address, until the freelancer claims the tab
  • Your list of tabs, which gives every invite its slot
  • Every payout, which the statement adds up

Passkey accounts are built with Mera (@category-labs/mera): its PRF calls give every key here, and a small WebAuthn client plugged into Mera asks for two salts in one prompt. Segundo runs on Monad testnet with test AUSD; nothing here is real money.

One fingerprint. Every key you need, and none you have to keep.