Device management
Enroll and revoke browser devices with non-extractable local keys while keeping plaintext vault root keys out of application servers.
Ownfold preferably enrolls a new browser through dedicated passkey vault access when WebAuthn PRF is available. Recovery Kit restoration and approval from an existing unlocked device remain fallbacks. Every successful path creates a non-extractable X25519 private key, wraps the root key for the corresponding public key, and sends only the public device record through the configured transport.
An existing unlocked browser can also approve an expiring pairing offer. The new browser never receives the Recovery Kit or recovery password:
const offer = await newBrowser.beginPairing()
if (offer.status === "error") return
const approved = await existingBrowser.approvePairing(
JSON.stringify(offer.value),
)
if (approved.status === "error") return
await newBrowser.refreshPairing()
Transfer the complete offer directly between browsers. Ownfold compares it byte-for-byte with the authenticated pending request before wrapping the root key. Offers expire after ten minutes by default and the server rejects lifetimes above fifteen minutes.
The host can render the public offer as text, a local QR code, or another application-selected transfer mechanism. QR generation should have no network dependency. The offer embeds no root key, Recovery Kit, recovery secret, or device private key. The approving browser still verifies it against authenticated server state before wrapping the vault key.
Client API
const devices = await vault.listDevices()
if (devices.status === "error") return
const device = devices.value.find((candidate) => candidate.deviceId === deviceId)
if (device?.status === "active") {
await vault.revokeDevice(device.deviceId, device.revision)
}
For one consistent settings view, prefer getDeviceInventory(). It returns active and revoked
devices, the local current device ID, a server snapshot time, and the vault revision. Rename either
the current browser or another active device with optimistic concurrency:
const inventory = await vault.getDeviceInventory()
if (inventory.status === "error") return
const device = inventory.value.active[0]
if (device) await vault.updateDeviceLabel(device.deviceId, "Work laptop", device.revision)
Labels are trimmed, internal whitespace is collapsed, control characters are rejected, and the
normalized value must contain 1–128 characters. A rename increments only the device revision. It
does not change device IDs, public keys, envelopes, authorization, root keys, or the vault
revision. VaultServer emits the secret-safe update-device-label audit operation.
revision is required for revocation so concurrent management sessions cannot silently overwrite
each other. Revoking the current browser clears its local identity and returns the client to
unavailable-on-device. Restore with passkey where supported, or use the Recovery Kit or pairing;
each path creates a new normal device ID.
React applications use useVaultDevices() for headless operations and decide which active or
revoked device metadata to display. The hook exposes inventory, rename, and diagnose in
addition to compatibility list/current/revoke actions. Ownfold refreshes the current device’s activity time during
verified initialization and unlock. The
timestamp is coordination metadata, is never trusted from the browser, and does not increment the
device’s security revision or invalidate a concurrent revocation action. Adapters preserve the
latest timestamp when requests arrive out of order or application-server clocks temporarily skew.
Server persistence
Apply 0001_ownfold_devices.sql, 0002_ownfold_pairings.sql,
0004_ownfold_device_last_active.sql, and 0007_ownfold_device_environment.sql after the v0.1 vault migration when using Drizzle or direct
PostgreSQL. Migration 0004 backfills existing devices from created_at before making the new
column required. Passkey access additionally requires 0005_ownfold_passkey_access.sql. Prisma
users should add OwnfoldDevice, OwnfoldPairing, OwnfoldPasskeyChallenge,
OwnfoldPasskeyAccess, and their relations from
the packaged schema, then apply the packaged Prisma migration or create an equivalent migration in
their normal workflow.
The security rationale and anti-substitution checks are specified in ADR 0006: Existing-device pairing. Dedicated recovery enrollment is specified in the Passkey vault access API.
The server can see device IDs, labels, optional environment metadata, public keys, creation, last-active and revocation times, status, revision, and encrypted envelope sizes. Environment metadata supports browser family/version, operating-system family, and a coarse device class. It is untrusted presentation data, is never used for authorization, and must not become an invasive fingerprint. A user-defined label remains the canonical display name. The server cannot open the device envelope.
diagnoseLocalDevice() reports aligned, missing-local, missing-server, revoked, public-key
mismatch, and different-vault states with typed repair paths. It never silently repairs or clears
identity; the host keeps user choice and policy.
Locking and current limitations
Paired browsers can unlock with their non-extractable device key because they do not receive a Recovery Kit. This is an application-level memory lock, not user-presence authentication: malicious same-origin JavaScript can invoke the same Web Crypto key while it is available. Host applications requiring OS user presence need a separately reviewed platform authenticator design.
The browser client automatically locks after fifteen idle minutes by default. Locking, current
device revocation, and local-state removal propagate across tabs through versioned, secret-free
messages. Call client.recordActivity() for activity outside normal document pointer and keyboard
events, and call client.dispose() when permanently tearing down a client.
The design and limitations are specified in ADR 0007: Browser lock coordination. Retention and deletion of revoked-device metadata are intentionally owned by the host application. Legal retention, security-audit, and incident-response requirements vary, so Ownfold adapters do not perform automatic destructive cleanup. Operators must apply and test their documented retention policy.