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.
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.
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.
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.
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.
| Key | PRF salt | When your passkey asks | Kept |
|---|---|---|---|
| Wallet key | mera.prf.salt.v1 | When you sign in, and after 15 minutes idle | Page memory, while you're signed in |
| Sealing key | segundo/seal/v1 | Its own prompt, when you open your details | Page memory, until the session locks |
| Invite keys | segundo/invite/v1 | Once a session, when you open or rebuild an invite | Page memory; the link itself in this browser |
| Disclosure key | segundo/disclose/v1 | Shares the sealing prompt: one fingerprint for both | Page 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
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
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
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
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
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
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
One job per key
A key that pays, a key that guards, a key that travels in links, a key that talks to strangers. Each is built for its job, with the algorithm the job needs: secp256k1 to sign on Monad, AES-GCM to seal, X25519 to share.
A leak stays where it happens
Different salts give unrelated outputs: the authenticator computes each with a secret it never releases. A forwarded invite link, a verifier's reply, even an exported wallet phrase reveal nothing about the other keys.
Prompts match promises
Signing in unlocks paying. Your details open only when you confirm for them. Where the authenticator allows, one fingerprint evaluates two salts at once (WebAuthn's second PRF input), so sharing details is a single confirmation.
Nothing to back up
No key is stored: each is computed when needed and dropped when the session locks. Your passkey, synced by your phone's password manager, is the only thing to keep.
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.