stable

magic-link

One-time URL that opens a noy-db vault in a viewer-scoped, TTL-bound session — no secret, no account.

@noy-db/on-magic-link · pnpm add @noy-db/on-magic-link One-shot viewer session via a single-use URL.

What it does

createMagicLinkToken(vault, options) mints a MagicLinkToken — a ULID token, the target vault, an expiresAt, and a role that's always 'viewer'. deriveMagicLinkKEK(serverSecret, token, vault) HKDF-derives the same viewer KEK on both server (at grant time) and client (at unlock time) from a server-held secret and the token — the ULID is hashed into the HKDF salt so it can't be trivially inverted, and the vault id is bound into the info field so a token for vault A can't unlock vault B. buildMagicLinkKeyring assembles the resulting UnlockedKeyring, always viewer-scoped. Single-use enforcement (marking a token consumed) is the server's job — isMagicLinkValid only checks expiresAt. A newer delegation bridge (issueMagicLinkDelegation / claimMagicLinkDelegation) layers scoped, tier-bound grants — whole-collection or single-record — on top of the same primitive.

Usage

import { createMagicLinkToken, deriveMagicLinkKEK } from '@noy-db/on-magic-link'

// Server: mint and send
const link = createMagicLinkToken('acme', { ttlMs: 7 * 24 * 60 * 60 * 1000 })
await emailViewer(`https://app.example.com/view?t=${link.token}`)

// Server + client both derive the same KEK from the token
const kek = await deriveMagicLinkKEK(serverSecret, link.token, 'acme')

See also