API reference

@wellknown-id/wallet/service

The wallet service: everything a wallet does, made once from the platform's ports. An app builds it with createWalletService(ports), calls its commands, listens for what changed (on), opens and closes it as the app comes to the front and goes, and puts to the holder what only they can answer (open's asks). The app itself keeps no state and makes no decisions: kivi is such an app, over its platform adapters.

Classes

PagesDifferError

Refused while the pages differ from the log: bonding, handing over the holder's wellknown.id, signing in for another device.

Extends

  • Error

Constructors

Constructor
new PagesDifferError(message?): PagesDifferError;
Parameters
ParameterType
message?string
Returns

PagesDifferError

Inherited from
Error.constructor
Constructor
new PagesDifferError(message?, options?): PagesDifferError;
Parameters
ParameterType
message?string
options?ErrorOptions
Returns

PagesDifferError

Inherited from
Error.constructor

Properties

PropertyModifierTypeDefault value
codereadonly"E_PAGES_DIFFER"'E_PAGES_DIFFER'

Interfaces

CredentialRegistry

Where the platform lets sites find this wallet's credentials (the Digital Credentials API: Android's Credential Manager). None in a browser.

Methods

register()
register(credentials): Promise<string[]>;

Registers these presentable credentials (a batch as one, under its batch id) in place of what was there. Resolves the ids registered.

Parameters
ParameterType
credentialsSdJwtVcCredential[]
Returns

Promise<string[]>

supported()
supported(): boolean;
Returns

boolean


HolderStore

Where the device keeps its HolderState: a file on a phone, localStorage in a browser.

Methods

load()
load(): Promise<HolderState | undefined>;
Returns

Promise<HolderState | undefined>

save()
save(state): Promise<void>;
Parameters
ParameterType
stateHolderState | undefined
Returns

Promise<void>


Passkeys

The holder's wellknown.id passkeys (prd.md §4.7, §4.10): the system's prompts, through the platform.

Methods

create()
create(user, exclude): Promise<{
  credentialId: string;
  prfEnabled: boolean;
  unlock?: Uint8Array<ArrayBufferLike>;
}>;

Makes a passkey for a keyring: exclude, the credential IDs it mustn't duplicate (E_EXCLUDED). Resolves its credential ID, whether it does PRF, and the unlock secret if the provider computed it at registration.

Parameters
ParameterType
userPasskeyUser
excludestring[]
Returns

Promise<{ credentialId: string; prfEnabled: boolean; unlock?: Uint8Array<ArrayBufferLike>; }>

tell()
tell(k): Promise<void>;

Tells the providers (the WebAuthn Signal API, where there is one) which passkeys open the keyring. Never throws.

Parameters
ParameterType
kKeyring
Returns

Promise<void>

unlock()
unlock(elsewhere?, credentialId?): Promise<{
  credentialId: string;
  secret: Uint8Array;
  user?: string;
}>;

The passkey's unlock secret (its PRF output), its credential ID (base64url) and its user handle where the system gives it, after the system's prompt. Any wellknown.id passkey, or credentialId's only; elsewhere lets the system offer one from another device (a QR code). Rejects with code E_CANCELLED, E_NO_PASSKEY or E_NO_PRF.

Parameters
ParameterType
elsewhere?boolean
credentialId?string
Returns

Promise<{ credentialId: string; secret: Uint8Array; user?: string; }>


QuietSealer

Seals a persona's records keys and the list of personas with a key bound to this device, without a prompt (prd.md §4.10): the Keystore on Android, a Secure Enclave key on iOS. None in a browser, where they're never kept: they come from the seed, which opens there without a prompt.

Methods

open()
open(sealed): Promise<Uint8Array<ArrayBufferLike>>;
Parameters
ParameterType
sealedUint8Array
Returns

Promise<Uint8Array<ArrayBufferLike>>

seal()
seal(data): Promise<Uint8Array<ArrayBufferLike>>;
Parameters
ParameterType
dataUint8Array
Returns

Promise<Uint8Array<ArrayBufferLike>>

Type Aliases

AfterRecovery

type AfterRecovery = object;

Back in through custodians (prd.md §7.11, after a recovery): the dealing whose shares travelled, its policy, and its custodians by contact, each with whether this device can reach them yet. Until it deals again, those shares still open the keyring as it was; it deals again by itself once it reaches every one (upkeep).

Properties

PropertyType
contactsobject[]
epochstring
replicasboolean
thresholdnumber
waitMsnumber

Asked

type Asked = object[];

What a site asked for, as the screens show it before the holder decides (wallet.ts prepareAnswer).

Type Declaration

NameType
credentialobject
credential.subtitlestring
credential.titlestring
pathsstring[][]
witness?string

Bequest

type Bequest = object;

What a beneficiary's device shows of a legacy: only once told, or once received.

Properties

PropertyType
error?string
fromobject
from.contact?string
from.namestring
note?string
received?string
secrets?string[]
seriesstring
toldboolean

Bringing

type Bringing = object;

A passkey the holder brought in that opens a second keyring (findWithPasskey): what merging does, and doing it.

Properties

PropertyType
planMergePlan

Methods

apply()
apply(): Promise<void>;
Returns

Promise<void>

cancel()
cancel(): void;
Returns

void


CheckSetup

type CheckSetup = object;

What the release watch checks (service/pages.ts): the release keys pinned (the key that signs now, then its successor) and the origins checked (by the manifest's names, with where to fetch each).

Properties

PropertyType
keysreadonly ReleaseKey[]
test?boolean
whereRecord<string, string>

CodeOutcome

type CodeOutcome = 
  | {
  id: string;
  kind: "added" | "restored" | "here";
  name: string;
}
  | {
  code: string;
  here: string;
  id: string;
  kind: "conflict";
};

What a recovery code entered here did, once the holder is reached.


Contact

type Contact = Omit<ContactView, "devices"> & object;

A contact, as the screens show it. waiting: chosen here, not in the keyring yet.

Type Declaration

NameType
devicesContactDeviceHere[]
waiting?boolean

ContactDeviceHere

type ContactDeviceHere = ContactView["devices"][number] & object;

A contact's device, as the screens show it: with this device's bond with it, if there is one.

Type Declaration

NameType
bondId?string

ContactList

type ContactList = object;

The holder's contacts, and the bonds with someone else's devices that belong to none yet.

Properties

PropertyType
contactsContact[]
unsortedBond[]

CustodyPublic

type CustodyPublic = Pick<CustodyNote, 
  | "epoch"
  | "threshold"
  | "count"
  | "waitMs"
  | "dealt"
  | "by"
  | "replicas"
  | "contacts"> & object;

The dealing to custodians, less its secrets (prd.md §7.11): what's read without the gate.

Type Declaration

NameType
custodiansnumber

DealingView

type DealingView = object;

A dealing as the dealer sees it: each custodian by contact.

Properties

PropertyType
byThisDeviceboolean
countnumber
custodiansobject[]
dealtstring
epochstring
replicasboolean
thresholdnumber
waitMsnumber

GateStatus

type GateStatus = "ready" | "no-lock" | "unavailable" | "browser";

Whether secrets can be gated here: 'ready' (a phone with a lock), 'no-lock', 'unavailable', or 'browser' (sealed, no prompt).


GiveAsk

type GiveAsk = object;

A browser's hand-over code, connected: the six digits, and the holder's answer.

Properties

PropertyType
codestring

Methods

cancel()
cancel(): void;
Returns

void

give()
give(): Promise<void>;
Returns

Promise<void>


GroupSignAsk

type GroupSignAsk = object;

What a member is asked to agree to, as their screen shows it before they decide.

Properties

PropertyType
actor?string
change?object
change.addednumber
change.membersnumber
change.removednumber
change.thresholdnumber
groupstring
labelstring
scope?string[]
site?string
until?number

HeldLegacy

type HeldLegacy = object;

A legacy share held here for someone, as its custodian sees it: whose (their contact), the terms, and where it stands. quiet: they've been active within the period (nothing to do). may-agree: the period has passed. agreed: this custodian agreed, and waits for enough others. window: enough agree; the release goes at ends unless they show a sign of life. released. unreadable: what's needed couldn't be read just now, so nothing moves.

Properties

PropertyType
addedAtstring
contact?object
contact.idstring
contact.namestring
countnumber
ends?number
idstring
quietMsnumber
state"quiet" | "may-agree" | "agreed" | "window" | "released" | "unreadable"
thresholdnumber
windowMsnumber

HeldShare

type HeldShare = object;

A custodian's view of a share held for someone: whose, by contact (prd.md §7.12).

Properties

PropertyType
addedAtstring
contact?object
contact.idstring
contact.namestring
copy?boolean
countnumber
dropped?string
epochstring
holderShareCredential["holder"]
idstring
indexnumber
pending?ShareCredential["pending"]
thresholdnumber
waitMsnumber

HolderAsk

type HolderAsk = 
  | {
  asked: Asked;
  bond: Bond;
  request: Extract<BondRequest, {
     kind: "openid4vp";
  }>;
  type: "proof";
  approve: Promise<void>;
  decline: void;
}
  | {
  bond: Bond;
  request: Extract<BondRequest, {
     kind: "return";
  }>;
  type: "return";
  approve: Promise<void>;
  decline: void;
}
  | {
  bond: Bond;
  site: string;
  type: "grant-key";
  approve: Promise<void>;
  decline: void;
}
  | {
  bond: Bond;
  by?: string;
  count: number;
  name: string;
  threshold: number;
  type: "group-invite";
  approve: Promise<void>;
  decline: void;
}
  | {
  bond: Bond;
  by?: string;
  group: string;
  label: string;
  site: string;
  type: "group-site-key";
  approve: Promise<void>;
  decline: void;
}
  | {
  asked: GroupSignAsk;
  bond: Bond;
  by?: string;
  type: "group-sign";
  approve: Promise<void>;
  decline: void;
};

A bonded device's request that only the holder can answer, put to them by the app's screens. Each is answered once: approve (which may ask the gate, and rejects, E_CANCELLED and the like, leaving it still asked) or decline.

Union Members

Type Literal
{
  asked: Asked;
  bond: Bond;
  request: Extract<BondRequest, {
     kind: "openid4vp";
  }>;
  type: "proof";
  approve: Promise<void>;
  decline: void;
}

Type Literal
{
  bond: Bond;
  request: Extract<BondRequest, {
     kind: "return";
  }>;
  type: "return";
  approve: Promise<void>;
  decline: void;
}

Type Literal
{
  bond: Bond;
  site: string;
  type: "grant-key";
  approve: Promise<void>;
  decline: void;
}

A contact asks for this holder's key at a site, to let them act for the contact there (prd.md §7.13).


Type Literal
{
  bond: Bond;
  by?: string;
  count: number;
  name: string;
  threshold: number;
  type: "group-invite";
  approve: Promise<void>;
  decline: void;
}

A contact (by, as the holder knows them) invites this holder into a shared identity (prd.md §7.14): its name, and how many of how many must agree.


Type Literal
{
  bond: Bond;
  by?: string;
  group: string;
  label: string;
  site: string;
  type: "group-site-key";
  approve: Promise<void>;
  decline: void;
}

A member of a shared identity asks for this holder's key at a site, for the group: it says who they are there to the other members.


Type Literal
{
  asked: GroupSignAsk;
  bond: Bond;
  by?: string;
  type: "group-sign";
  approve: Promise<void>;
  decline: void;
}

A member asks this holder to agree: a grant for the group at a site, or a change of its members.


HolderState

type HolderState = object;

What a device keeps of the holder's wellknown.id: the default persona's seed, sealed by this device's gate; this device's own merged keyring (prd.md §4.10), sealed under a key from that seed, so it opens only once the gate has opened the seed; and, on a phone, each persona's records keys and the list of personas, sealed by a hardware key without a prompt (never in the clear). Private to the device.

Properties

PropertyTypeDescription
codesChecked?numberWhen the recovery-code copies were last checked against the keyring here (ms; keyring-holder.ts checkCodes).
credentialId?stringThe passkey that reached the seed, where one did; with until, the end of its session where the app keeps the seed only for a while (the sign-in pages' unlock session, keyring-holder.ts sessionMs).
custodyQuiet?stringOn a phone: the dealing's quiet part (service/holder.ts CustodyQuiet), sealed by the quiet sealer, so notices are read without the gate.
how"passkey" | "recovery-code" | "new" | "bond" | "custodians" | "browser"-
keyring?stringThe keyring as this device last had it (base64: keyring.ts sealKeyringHere under the seed).
later?string[]-
laterKey?stringOn a phone: the public key what bonded devices send about personas while the gate is closed is sealed to (persona-bonds.ts laterKeys, from the default's seed; base64), and what's waiting, sealed (sealForLater), merged the next time the gate opens the seed.
legacyNotices?LegacyNotice[]-
legacyQuiet?stringOn a phone: the legacy dealings' quiet parts (service/holder.ts LegacyQuiet), sealed by the quiet sealer.
legacySeq?Record<string, number>Each legacy notice box, as far as this device has read it (by series), and what custodians said there.
lifeAt?numberWhen this device last wrote a sign of life (prd.md §7.11, legacy; ms).
mailboxes?object[]On a phone: each held persona's records keys (seed.ts recordsKeys), the default's first, sealed by the quiet sealer (base64), with the persona's id. Absent in a browser, where they come from the seeds.
notices?RecoveryNotice[]-
noticeSeq?numberThe notice box, as far as this device has read it (the item sequence), and the recoveries it has heard of.
recoveredBy?stringThe recovery this device got back in by (its id): its notices are this device's own, not one to stop.
redeal?stringBack in through custodians (prd.md §7.11): the dealing whose shares travelled, until one is dealt again (here or on another of the holder's devices). Until then those shares still open the keyring as it was.
sealedSeedstringbrowser: a seed made in a browser that can't do passkeys with PRF, kept there (the sign-in pages).
several?booleanWhether the holder has more than one persona in use: all a sign-in page knows of them before the keyring opens.
sincestring-
summary?stringOn a phone: the list of personas (PersonaList: names, states, fingerprints, no seeds or keys), sealed the same way.
told?stringWhat the passkey providers were last told (passkeys.ts signalled), so they're told again only when it changes.
until?number-
vaultKeys?stringThe vault keys (prd.md §7.11; custodians.ts): the recovery key and the secrets key, with the dealing's epoch, sealed under the default persona's seed as keyring is (never in the keyring, which a passkey opens). Only on the holder's own devices, brought by their bonds.
writer?stringThis device's writer id for keyring edits (keyring.ts newWriter).

Joining

type Joining = object;

The side that joins, once connected: the six digits to compare, what the other side said, and the holder's answer. Bonding as someone else's, confirm takes the contact the other device belongs to (prd.md §7.12).

Properties

PropertyType
codestring
kindBondKind
peerDevice
peerDidstring
personasPersonaLines

Methods

cancel()
cancel(): void;
Returns

void

confirm()
confirm(contact?): Promise<Bond>;
Parameters
ParameterType
contact?ContactPick
Returns

Promise<Bond>


LegacyNotice

type LegacyNotice = object;

A legacy custodian's agreement to a release, as the holder's notice box told this device (prd.md §7.11).

Properties

PropertyType
atnumber
bystring
contactstring
epochstring
seriesstring

LegacyPublic

type LegacyPublic = Pick<LegacyNote, 
  | "series"
  | "epoch"
  | "threshold"
  | "count"
  | "quietMs"
  | "windowMs"
  | "dealt"
  | "by"
  | "contacts"
  | "told"
| "replicas">;

A legacy dealing, less its secrets (prd.md §7.11): what's read without the gate, by the beneficiary's contact.


LegacyView

type LegacyView = object;

A legacy dealing, as the holder's devices show it: for whom, to whom, on what terms, and what's left to them.

Properties

PropertyTypeDescription
beneficiaryobject-
beneficiary.idstring-
beneficiary.namestring-
countnumber-
custodians?object[]Only on the device that dealt: each custodian's answer, as recovery's dealing view says it.
dealtstring-
epochstring-
quietMsnumber-
secretsobject[]The secrets kept here that are left to them, by name.
seriesstring-
thresholdnumber-
toldboolean-
windowMsnumber-

OfferAsk

type OfferAsk = object;

An offer held here, put to the holder: keep it (behind the gate) or decline.

Properties

PropertyTypeDescription
bondBond-
contact?stringFrom someone else's device (a copy given, prd.md §7.14): their name as this holder knows them, where they're a contact.
offerOffer-

Methods

decline()
decline(reason): void;
Parameters
ParameterType
reasonstring
Returns

void

keep()
keep(): Promise<void>;

Keeps it here (the gate asks now, to seal it) and says so to the device that offered it. Rejects (E_CANCELLED…) and stays asked if it can't.

Returns

Promise<void>


PagesCheck

type PagesCheck = object;

Where the release watch stands, for the screens.

Properties

PropertyTypeDescription
checkingbooleanA check is running.
originsstring[]The origins it checks.
standing?Standing-
status"off" | "never" | "ok" | "differs"off: nothing to check here; never: no check has got an answer yet.
test?booleanPointed at a test stack (a development build).
unchecked?objectThe latest try got no answer: when, and why.
unchecked.atnumber-
unchecked.reasonstring-
webbooleanWhere this wallet runs, for what the screens say about the check's limits.

PasskeyUser

type PasskeyUser = object;

The WebAuthn user entity of a passkey made for a keyring (passkeys.ts passkeyUser): its ring as the handle, base64url.

Properties

PropertyType
displayNamestring
idstring
namestring

PersonaList

type PersonaList = object;

The personas as the screens show them, the passkeys that open the keyring, whether their names carry the default's name, the dealing, and the contacts (prd.md §7.12).

Properties

PropertyType
contacts?ContactView[]
custody?CustodyPublic
grants?GrantView[]
groups?GroupView[]
legacy?Record<string, LegacyPublic>
names?boolean
passkeysobject[]
personasPersonaView[]
tombs?ContactTombs

Recognising

type Recognising = object;

A recovery a custodian is taking part in, before confirming: the code to compare.

Properties

PropertyType
codestring
recoverystring

Methods

cancel()
cancel(): void;
Returns

void

confirm()
confirm(): Promise<void>;
Returns

Promise<void>


Recovery

type Recovery = object;

A recovery begun on this device: the link to give custodians, the code, and how far it has got.

Properties

PropertyType
codestring
done?boolean
error?string
idstring
sharesnumber
sincestring
threshold?number
urlstring

RecoveryNotice

type RecoveryNotice = object;

A recovery under way, as a custodian's notice told this device (prd.md §7.11), and whether the holder stopped it.

Properties

PropertyType
atnumber
bystring
codestring
epochstring
recoverystring
stopped?number

ReleasePorts

type ReleasePorts = object;

What the release watch needs from the platform: what to check, a fetch, and somewhere to keep its state.

Properties

PropertyTypeDescription
fetchReleaseFetchA fetch for the check: fresh (no cache), no credentials, a timeout; rejects when there's no answer.
webbooleanThis wallet is the web app (what the screens say about the check's limits).

Methods

load()
load(): Promise<unknown>;

What the watch keeps between checks, on this device only.

Returns

Promise<unknown>

save()
save(state): Promise<void>;
Parameters
ParameterType
stateunknown
Returns

Promise<void>

setup()
setup(): CheckSetup | undefined;

What this device checks; undefined: nothing here.

Returns

CheckSetup | undefined


Say

type Say = (key, values?) => string;

Puts one of the service's words (SayKey) in the holder's language, with its values filled in.

Parameters

ParameterType
keySayKey
values?Record<string, string | number>

Returns

string


SayKey

type SayKey = 
  | "reason.keep"
  | "reason.default"
  | "reason.records"
  | "reason.personas"
  | "reason.recoveryCode"
  | "reason.give"
  | "reason.signIn"
  | "reason.bond"
  | "reason.givePersonas"
  | "reason.deal"
  | "reason.keepShare"
  | "reason.stop"
  | "reason.recoverable"
  | "reason.contacts"
  | "reason.keepLegacyShare"
  | "reason.legacy"
  | "reason.releaseLegacy"
  | "error.notBeneficiary"
  | "error.shortQuiet"
  | "error.noHolder"
  | "error.badRecoveryCode"
  | "error.notReached"
  | "error.noPersona"
  | "error.badRecoveryLink"
  | "error.noRequest"
  | "error.notCustodians"
  | "error.shortWait"
  | "error.noContact"
  | "pages.refused"
  | "custody.noScreenLock"
  | "custody.notShared"
  | "reason.grant"
  | "reason.grantBack"
  | "reason.grantDrop"
  | "reason.grantKeep"
  | "reason.grantForget"
  | "reason.grantKey"
  | "grants.noConfig"
  | "grants.notAccepted"
  | "grants.noContact"
  | "grants.unknownScope"
  | "grants.tooLong"
  | "grants.noDevice"
  | "grants.noAnswer"
  | "grants.unknown"
  | "grants.notAContact"
  | "grants.notForMe"
  | "shares.noContact"
  | "shares.noSecret"
  | "shares.noDevice"
  | "reason.groupFound"
  | "reason.groupJoin"
  | "reason.groupNews"
  | "reason.groupSite"
  | "reason.groupAgree"
  | "reason.groupGrant"
  | "reason.groupChange"
  | "groups.unknown"
  | "groups.noDevice"
  | "groups.noAnswer"
  | "groups.noName"
  | "groups.badThreshold"
  | "groups.notEnough"
  | "groups.notSigned"
  | "groups.notAContact"
  | "groups.notAgreed"
  | "groups.notFounded"
  | "groups.notMember";

The words the service puts in front of the holder: the gate's prompts, and errors. In the holder's language.


Secret

type Secret = object;

A secret as the holder enters it: its name, kind and value, and where it's for.

Properties

PropertyType
copyOf?string
fileName?string
namestring
secretTypeSecretCredential["secretType"]
service?string
valueUint8Array

ServiceEvent

type ServiceEvent = 
  | "log"
  | "bonds"
  | "offers"
  | "pages"
  | "holder"
  | "records"
  | "custody"
  | "contacts"
  | "legacy"
  | "grants"
  | "groups";

What changed, for screens to read again: the log (and so the secrets catalogue), bonds, offers held, the release watch, the holder's state here, the personas' records (this device's consents filed in them), the custodians (shares offered, held or released, a dealing, a recovery under way, prd.md §7.11), the contacts (§7.12).


ServicePorts

type ServicePorts = object;

Everything the service needs from the platform, and nothing else: an app supplies these adapters (a browser's, a phone's, an in-memory set for tests) and the service does the rest.

Properties

PropertyTypeDescription
bondsBondStore & LogStore & OfferStoreBonds (with update), the sync log, offers held.
clock?ClockThe time; tests move it.
credentialsCredentialStoreDocuments, secrets' notes, consents.
custodyLimits?() => objectThe wait a dealing may be made with, at least (prd.md §7.11: 72 hours); a development build's tests shorten it.
deviceDeviceWhat this device calls itself, and its platform.
dropsDropStoreDrop boxes at the relay: what bonded devices leave for this one while it's away.
fetch?typeof fetchFetch, for sites' privacy declarations and witnesses' discovery.
gateSealerThe biometric gate (a browser: a non-extractable key, no prompt). Seals secrets and the holder's seed.
holderStateHolderStoreThe holder's state on this device (the default persona's sealed seed, this device's keyring copy).
keysKeyStoreThe device's identity key and credential keys.
passkeysPasskeysThe platform's passkeys, with PRF: the holder's unlock.
push?objectThis device's push token as last fetched, straight away (for answering pings).
push.knownPushHandle | undefined-
quiet?QuietSealerOn a phone: what's read without the gate. None in a browser.
registry?CredentialRegistryWhere sites find this wallet's credentials (Android's Credential Manager); none in a browser.
relayRelayThe relay's mailboxes.
release?ReleasePortsThe release watch: kivi's check of the pages wellknown.id serves against the signed release log.
saySayThe service's words, in the holder's language.
storeEncryptedStoreThe encrypted store (keyrings, recovery-code copies, the personas' mailboxes) and drop boxes, at the relay.
trust?TrustStoreDocument signers' trust anchors (CSCA certificates), for checking passports and ID cards.

Methods

gateStatus()
gateStatus(): Promise<GateStatus>;

Whether secrets can be gated here, and how.

Returns

Promise<GateStatus>

wake()
wake(bond): Promise<boolean>;

Asks the relay to wake a bonded phone (a content-free push). False if it can't.

Parameters
ParameterType
bondBond
Returns

Promise<boolean>


ShareAsk

type ShareAsk = object;

A share offered to this device, put to its holder. contact: whose, as this holder knows them (none yet: accepting takes a name, and makes them a contact). copy: sent by another of this holder's own devices, which kept it there. devices: this holder's other devices it could be kept on too. Accept (with a name, if there's no contact; and the other devices chosen), or decline.

Properties

PropertyTypeDescription
bond?Bond-
contact?object-
contact.idstring-
contact.namestring-
copy?boolean-
devicesobject[]-
fromstring-
legacy?objectA share of a legacy key (prd.md §7.11): the period of quiet and the veto window it's held on. Absent for the recovery key.
legacy.quietMsnumber-
legacy.windowMsnumber-
sharePick<ShareOffer, "threshold" | "count" | "waitMs">-

Methods

accept()
accept(o?): Promise<void>;
Parameters
ParameterType
o?{ keepOn?: readonly string[]; name?: string; }
o.keepOn?readonly string[]
o.name?string
Returns

Promise<void>

decline()
decline(reason): void;
Parameters
ParameterType
reasonstring
Returns

void


SignInAsk

type SignInAsk = object;

A sign-in page's request, connected: the site, which persona would sign (the chooser), and the holder's answer.

Properties

PropertyTypeDescription
actingobject[]Contacts who've let this holder act for them at the site (prd.md §7.13), by grants held here, and shared identities whose members agreed to let this holder act for them there (§7.14): each a choice beside signing in as themselves, never made for them.
choice?Choice-
name?string-
sitestring-

Methods

decline()
decline(reason?): void;
Parameters
ParameterType
reason?string
Returns

void

sign()
sign(persona?, grant?): Promise<void>;

Signs as persona (the chooser's, if none is named), behind the gate, and sends only the self-issued token; then files the sign-in in that persona's mailbox. With grant (one of acting's ids), acts for that contact: the persona the grant names signs, and the grant goes with the token. Rejects E_CANCELLED (still asking) if the holder closes the prompt.

Parameters
ParameterType
persona?string
grant?string
Returns

Promise<void>


SiteTerms

type SiteTerms = Delegation & object;

What a site says about delegation, as the screens show it before a grant is made.

Type Declaration

NameType
sitestring

Standing

type Standing = object;

The result of the latest check that got an answer: the pages served as the signed release lists them, or not.

Properties

PropertyType
atnumber
checkednumber
commit?string
differences?Difference[]
release?number
rollingOut?string[]
scopeScope
status"ok" | "differs"

WalletService

type WalletService = ReturnType<typeof createWalletService>;

The wallet service: its commands, grouped by what they're about (holder, bonds, contacts, custodians…).

Variables

CHECK_STANDS_MS

const CHECK_STANDS_MS: number;

How long a check's result stands before the next bonding or sign-in checks again.


DAILY_MS

const DAILY_MS: number;

How often the app checks while open, and checks fonts and images too.

Functions

createWalletService()

function createWalletService(ports): object;

Makes the wallet service from the platform's ports: once per app, as a second one over the same storage would answer the same bonds twice. Two services over the same storage behave like the same device restarted.

Parameters

ParameterType
portsServicePorts

Returns

NameTypeDefault valueDescription
bondsobject--
bonds.deviceNames()() => Promise<Map<string, string>>sync.deviceNamesThe devices this one knows by name, by did:key: those its bonds name, and those the logs held here describe. Who signed a record (records.ts whoSigned) is named from it.
bonds.gaps()() => Promise<Gap[]>sync.gaps-
bonds.give()(bond, ids) => Promise<void>-Gives a limited bond's device personas; takes them back (a request to forget).
bonds.list()() => Promise<Bond[]>--
bonds.personasToGive()() => Promise<PersonaView[]>holder.personasToGiveThe personas this device could give a limited device. The personas this device could give a limited device: those in use, by name.
bonds.reachable()(bond, timeoutMs?) => Promise<boolean>-Whether bond's device is there to answer now (its answer brings its current push token, kept on the bond).
bonds.remove()(bond) => Promise<void>sync.removeDeviceRemoves a bonded device: one of the holder's own leaves all their devices; anyone else's, just this one.
bonds.takeBack()(bond, ids) => Promise<void>--
bonds.untilReachable()(bond, timeoutMs?, cancelled?) => Promise<boolean>-Waits for bond's device to be there, pinging every couple of seconds; stops early if cancelled().
bonds.wake()(bond) => Promise<boolean>-Asks the relay to wake bond's device (with its push token as last heard). False if it can't.
join()(url) => Promise<Joining>-The side that joins: from a scanned code (or its link), connects, and says what the other side asks. Refused while the pages differ.
pair()( kind, given?, origin?, contact? ) => Promise<Pairing>-The side that starts: the QR code to show (and its link), then the six digits, then the bond. kind: what the other device will be; given: the personas a limited device is given; contact: someone else's device's contact, chosen before the code is shown (prd.md §7.12). Refused while wellknown.id's served pages differ from its release log. origin: the web app that opens the link on a device without the app.
reach()(bond, o?) => Promise<void>-Makes sure the app is open on bond's device before asking it anything. If it isn't (notThere), wakes it (a notification that only says a request is waiting) where it can be (woken: whether it could), and waits up to the notification's two minutes for the holder to open it. Rejects if it never comes.
close()() => void-The app has gone to the background.
contactsobject-Contacts (prd.md §7.12): the people the holder knows, each a name only the holder's devices see, over the bonds with their devices. Chosen at bonding (bonds.pair, Joining.confirm); choose gives one to a bond that has none.
contacts.choose()(bond, pick) => Promise<string>contacts.chooseWhich contact bond's device is (prd.md §7.12): one the holder has, or a new one by the name they typed (never the device's). Kept on the bond now, and taken into the keyring as soon as it can be.
contacts.list()() => Promise<ContactList>contacts.listThe holder's contacts, without asking (see above).
contacts.merge()(from, into) => Promise<void>contacts.merge-
contacts.move()(did, into) => Promise<void>contacts.move-
contacts.remove()(id, opts) => Promise<void>contacts.removeRemoves a contact: what they told forgotten, the bonds made from contacts with their devices ended; with bonds (the default), the bonds with their devices made by a ceremony too, on each of the holder's devices.
contacts.rename()(id, name) => Promise<void>contacts.rename-
contacts.tell()(id, on) => Promise<void>contacts.tellThe holder's answer to "tell them about my other devices?" (asked once per contact). Yes sends them now; no after a yes asks their devices to forget what they were told, and ends the bonds made from it (settle).
custodiansobject-Custodians (prd.md §7.11): dealing shares of a recovery key to people the holder chooses, holding theirs, and getting back in through them.
custodians.afterRecovery()() => Promise<AfterRecovery | undefined>custodians.afterRecoveryBack in through custodians, and not dealt again yet: the dealing to retire, and whom this device reaches.
custodians.callOff()(shareId) => Promise<void>custodians.callOffA recovery under way through this custodian, called off here (before its release).
custodians.cancelRecovery()() => Promise<void>custodians.cancelRecovery-
custodians.checkNotices()() => Promise<boolean>custodians.checkNoticesReads the notice box (from where this device left off) and the veto box: recoveries under way, and stops.
custodians.confirmHeld()() => Promise<void>custodians.confirmHeld-
custodians.deal()(o) => Promise<DealingView>custodians.dealDeals to custodians (prd.md §7.11): contactIds the people (§7.12), each bonded here through at least one of their devices, any threshold of whom get the holder back in, after waitMs. A dealing already made is ended first (dealt again: a new key, a new epoch; its vault removed, custodians left out told to drop their shares). The gate asks once.
custodians.dealing()() => Promise<DealingView | undefined>custodians.viewThe current dealing, as the dealer sees it: each custodian's answer and last confirmation (stale after 60 days).
custodians.dealWithout()(id) => Promise<DealingView>custodians.dealWithoutDeals again without contact id (prd.md §7.12: removing a contact who is a custodian): the same policy to the others, the threshold no more than are left. Throws if fewer than MIN_THRESHOLD would be left. The dealing before is ended as any dealing again ends it: the one left out told to drop their share.
custodians.dropHeld()(id) => Promise<void>custodians.dropHeldA share this custodian wants to delete: told to the holder (as no longer held) the next time they ask.
custodians.held()() => Promise<HeldShare[]>custodians.heldThe shares this device holds for others (accepted), newest first.
custodians.markRecoverable()(secretId, on) => Promise<void>custodians.markRecoverableMarks a secret kept here recoverable (prd.md §7.11): its value goes into the secrets vault under the secrets key (made now if there's none yet; a dealing carries it in the vault). Or takes it out again. The gate asks.
custodians.nextAsk()() => Promise<ShareAsk | undefined>custodians.nextAskThe share waiting longest for this device's holder, if any, to put to them.
custodians.notices()() => Promise<RecoveryNotice & object[]>custodians.noticesThe recoveries this device has heard of, newest first, each custodian named as the holder knows them (prd.md §7.12).
custodians.pollRecovery()() => Promise<boolean>custodians.pollRecoveryReads the release box: each release that opens and verifies is kept, by index; with the threshold of one dealing, the key is rebuilt, the newest vault that opens found (the store's, the replicas'), the keyring and the vault keys kept, and the recoverable secrets brought back. Resolves true once the holder is reached.
custodians.recognise()(contactId, link) => Promise<Recognising>custodians.recogniseThe holder has lost everything and shows this custodian a recovery link (prd.md §7.11): whose it is, by contact (§7.12), picks the share held for them; the new device's key is read from the request box, and the code both screens show is returned to compare, in person or on a call. confirm posts the notice to the holder's devices and starts the wait; nothing is released before it ends.
custodians.recovery()(origin?) => Promise<Recovery | undefined>custodians.recoveryThe recovery begun here, if any, and how far it has got.
custodians.releaseDue()() => Promise<number>custodians.releaseDueFor each recovery waiting here: the veto box read (a stop by the veto key withholds the share); past the wait, with the veto box read successfully just now, the share released to the new device's key alone, with a signed statement. The gate opens the share for that. Returns how many were released.
custodians.startRecovery()(origin?) => Promise<Recovery>custodians.startRecoveryBegins getting back in through custodians (prd.md §7.11), on a device that hasn't reached the holder: a fresh key pair for the recovery, its public key in the request box, the link to give each custodian, and the code every screen will show. The release box is polled while the service is open.
custodians.stop()(recovery) => Promise<void>custodians.stopStops a recovery (prd.md §7.11): a veto signed with the keyring's veto key (the gate asks), for every custodian to read.
debugobject-For development tools: this device's latest asks, and what each listener is doing.
debug.asked()() => object[]--
debug.listening()() => Map<string, { at: number; state: string; }>--
declared()(origin) => Promise<Privacy | null>-What a site says it does with what it asks for: its config's privacy block (null: it publishes none).
deviceDeviceports.deviceWhat this device calls itself (the device port).
gateStatus()() => Promise<GateStatus>-Whether secrets can be gated here (the gate port's status).
giveobject--
connect()(url) => Promise<GiveAsk | undefined>-Connects to the browser showing url. Undefined if this device hasn't reached the holder. Refused while the pages differ.
grantsobject--
grants.drop()(id) => Promise<void>delegation.dropStops acting for a contact under a grant held here: ended in the keyring (the giver isn't asked anything).
grants.give()(o) => Promise<string>delegation.giveLets the contact act for this holder at site for days, doing what scopes (the site's names) say. Asks the contact's device for their key there first (their kivi asks them, up to waitMs). Resolves the grant's id.
grants.list()() => Promise<GrantView[]>delegation.listThe grants as the keyring has them (without asking: from the list the holder already sees).
grants.takeBack()(id) => Promise<void>delegation.takeBackTakes a grant back: gone from its mailbox at once, ended in the keyring, and the contact's devices asked to forget it.
grants.terms()(site) => Promise<SiteTerms>delegation.termsThe site's terms for delegation, from its wellknown.id config. Throws if it has none, or doesn't accept it.
groupsobject-Shared identities (prd.md §7.14): founded with contacts, acting at a site through a grant to one member that k members sign, members changed by k of them. Built, and not offered until delegation's legal review (the app decides).
groups.act()(o) => Promise<string>groups.actAsks to act for the group at site for days, doing what scopes (the site's names) say: the members' keys there, then their agreement, until k agree (each member's kivi asks them, up to waitMs). Resolves the grant's id.
groups.changeMembers()(o) => Promise<void>groups.changeMembersChanges the group: remove (members' keys), add (contacts, invited first), threshold. Signed by k members of the statement before (each asked). Resolves when it's agreed and the members are told.
groups.found()(o) => Promise<string>groups.foundFounds a shared identity with contactIds, threshold of all its members (the holder too) to agree. Each contact's kivi asks them; those who say yes are its founding members (it needs two members and threshold). Resolves its id here.
groups.leave()(group) => Promise<undefined>groups.leave-
groups.list()() => Promise<GroupView[]>groups.listThe groups as the screens show them (without asking: from the list the holder already sees).
groups.rename()(group, label) => Promise<undefined>groups.rename-
groups.takeBack()(group, id) => Promise<void>groups.takeBackTakes one of the group's grants back (any member may): gone from its mailbox at once, ended, the members told.
holderobject--
holder.addPasskeyHere()() => Promise<void>holder.addPasskeyHereAdds a passkey to the holder's keyring, deliberately (another provider, say): made with the keyring's user handle and names, and excludeCredentials listing its passkeys, so a provider that already keeps one refuses (E_EXCLUDED). Its PRF output files a copy of the keyring.
holder.addPersona()(name) => Promise<string>holder.addPersonaAdds a persona named name (made unique). Resolves its id.
holder.addPersonaWithCode()(code) => Promise<CodeOutcome>holder.addPersonaWithCodeA recovery code entered once the holder is reached: its persona joins the keyring, in use. A forgotten persona comes back (its code is the deliberate act that may). One already here under another name is the rename conflict, settled by settleConflict.
holder.defaultPersonaId()() => Promise<string | undefined>holder.defaultPersonaIdThe default persona's id (its fingerprint, in full), which a home seal is drawn from (prd.md §4.10). Read without asking: on a phone from the quietly sealed list of personas, in a browser from the default's seed. Undefined while no persona is held here (or, on a phone, before that list has been sealed).
holder.findWithPasskey()(elsewhere) => Promise<Bringing | undefined>holder.findWithPasskey-
holder.forget()() => Promise<void>holder.forgetForgets the holder's wellknown.id on this device (their personas stay reachable through their unlocks).
holder.forgetPersona()(id) => Promise<undefined>holder.forgetPersona-
holder.makeDefault()(id) => Promise<undefined>holder.makeDefault-
holder.makeRecoveryCode()(id?) => Promise<{ code: string; name: string; short: string; }>holder.makeRecoveryCodeA new recovery code for a persona (the default, if none is named), filed in the store (with its name), noted in the keyring and in every passkey's copy of it. Resolves what to show once: the code, the persona's name and fingerprint.
holder.openRecords()() => Promise<void>holder.openRecordsOn a phone whose personas aren't sealed for quiet reading yet: opens the seed once, through the gate, to seal them.
holder.personaList()() => Promise<PersonaList | undefined>holder.personaListThe holder's personas, without asking (personaList, below).
holder.personaSites()() => Promise<Map<string, number>>holder.personaSitesHow many sites each persona has been used at, from its mailbox (sign-ins and consents): by persona id.
holder.records()() => Promise<MailboxEntry[]>holder.recordsEverything in the personas' mailboxes, each record with the device that signed it (verified): sign-ins from the holder's browsers (unsigned), consents and sign-ins from every wallet holding the persona. A record in two mailboxes (filed again after the default changed) is listed once.
holder.recordsWaiting()() => Promise<boolean>holder.recordsWaitingWhether the records and the personas wait for the gate (a phone where they aren't sealed for quiet reading yet).
holder.removeRecoveryCode()(id, copyId) => Promise<void>holder.removeRecoveryCodeRemoves one of a persona's recovery codes (prd.md §4.10): marked removed in the keyring (every passkey's copy hears it), then its copy deleted from the store, so the code opens nothing. Should the app stop between the two, the next device to check its codes deletes it.
holder.rename()(id, name) => Promise<void>holder.renameRenames a persona; every recovery-code copy of it the keyring knows is rewritten with the new name.
holder.reuse()(id) => Promise<undefined>holder.reuse-
holder.setAside()(id) => Promise<undefined>holder.setAside-
holder.setNames()(on) => Promise<undefined>holder.setNames-
holder.settleConflict()(keepThisName) => Promise<void>holder.settleConflictThe rename conflict's answers: keep the name here (the code's copy rewritten with it), or rename back to the code's.
holder.startNew()() => Promise<void>holder.startNewFor someone new to wellknown.id: makes their passkey, and their first persona with it. The unlock comes with the passkey when its provider computes PRF at registration, so they're asked once; else the passkey is asked for it.
holder.state()() => Promise<HolderState | undefined>holder.stateWhat this device knows of the holder. What an older version kept that this one doesn't read (records keys in the clear; one persona's sealed records keys) is let go of at once (a clean break).
holder.unlockWithPasskey()(elsewhere) => Promise<void>holder.unlockWithPasskeyWith the holder's passkey (from this phone, or elsewhere through the system's QR code), finds their keyring. Rejects with code E_NO_HOLDER if the passkey opens none (they're new: see startNew).
holder.unlockWithRecoveryCode()(code) => Promise<void>holder.unlockWithRecoveryCodeWith a recovery code, on a device that hasn't reached the holder yet: the persona it's for, kept here as its own.
legacyobject-Legacy (prd.md §7.11): chosen secrets left to a beneficiary, released by custodians on a long absence they agree on, after a veto window; never the identity. Built, and not offered until the legal review (the app decides whether to show it).
legacy.agree()(shareId) => Promise<void>legacy.agreeThis custodian agrees to a release (prd.md §7.11): only once the period of quiet has passed. Signed with its agreement key, left in the agreement box (the other custodians read it) and the holder's notice box (their devices hear of it, and any sign of life voids it).
legacy.bequests()() => Promise<Bequest[]>legacy.bequestsWhat this device shows of legacies left to its holder: those the holder chose to tell them of, and those received.
legacy.checkNotices()() => Promise<boolean>legacy.checkNoticesReads each legacy's notice box from where this device left off: the custodians' agreements, by who they're left to.
legacy.checkReleases()() => Promise<number>legacy.checkReleases-
legacy.deal()(o) => Promise<LegacyView>legacy.dealDeals a legacy key for beneficiary (a contact) to custodians (contactIds, other contacts, each reached here), any threshold of whom may release it to the beneficiary alone, after quietMs without a sign of life and a veto window of windowMs. The beneficiary's devices are given the box secret quietly, and told they're named only if tell. Dealt again for the same beneficiary: a new key and epoch; the head before removed, its custodians told to drop their shares. The gate asks once.
legacy.end()(beneficiary) => Promise<void>legacy.endEnds the legacy for beneficiary: the keyring's note gone, the head removed, its custodians told to drop their shares, the beneficiary's devices told to forget the box secret (a compliant kivi does when it hears; it could have kept a copy, which opens nothing without the custodians). The secrets stay where they're kept. The gate asks.
legacy.held()() => Promise<HeldLegacy[]>legacy.heldThe legacy shares this device holds for others, each with where it stands (read now).
legacy.imHere()() => Promise<number>legacy.imHere-
legacy.leave()(secretId, beneficiary, on) => Promise<void>legacy.leaveLeaves a secret kept here to beneficiary (prd.md §7.11), or takes it back: its value into their content (under the content key, made now if there's none yet), or out of it. The gate asks.
legacy.list()() => Promise<LegacyView[]>legacy.viewsThe legacies the keyring notes, each with its terms and what's left to the beneficiary from here.
legacy.notices()() => Promise<LegacyNotice & object[]>legacy.noticesThe custodians' agreements this device has heard of, newest first, each with who it's left to, by name.
legacy.releaseDue()() => Promise<number>legacy.releaseDueFor each legacy share this custodian agreed to release: past the veto window, with the life entry, the veto box and the agreement box read in this same run and still enough agreements since the latest sign of life, the share sealed to the release key alone, with a statement. The gate opens the share. Resolves how many were released.
legacy.signOfLife()(force) => Promise<number>legacy.signOfLifeA sign of life (prd.md §7.11): written to each legacy's life entry at most daily (or now, force: "I'm here", also into the veto box), from any of the holder's devices that holds the keyring, without the gate. The dealing device also sends one over its bonds with the custodians, at most monthly. Resolves how many legacies it was written for.
legacy.withoutContact()(id, o) => Promise<{ dealtAgain: string[]; ended: string[]; tooFew: string[]; }>legacy.withoutContactRemoving a contact (prd.md §7.12): a legacy left to them ends; one they hold a share of is dealt again without them (dealAgain), where enough custodians are left (else it stays as it is, their share with them until it's dealt again, and tooFew names it). Only the device that dealt it knows its custodians: elsewhere those are left alone.
offersobject--
offers.askBack()(bond, remoteId, name) => Promise<Uint8Array<ArrayBufferLike>>offers.askBack-
offers.keepSecret()(secret, where) => Promise<{ deliveries: Delivery[]; record: SecretCredential; }>offers.keepSecretKeeps a new secret: here first if the holder chose (so leaving part way loses nothing), then offered to each of devices at once (open: whether each answered the form's last check). Resolves once every offer is handed over, live or into a drop box, with the note of the secret and how each went; then it's in this device's log and on its way to the others. Nobody's answer is waited for. Throws if the secret ends up nowhere at all.
offers.nextAsk()() => Promise<OfferAsk | undefined>offers.nextAskThe offer waiting longest for the holder here, if any, to put to them. Where this device can't gate secrets (a phone without a screen lock), it's declined at once, and the next is looked at.
on()(event, fn) => () => void--
open()(asks) => void-The app is open and in front (requests.ts): it listens for its bonded devices; asks puts to the holder what only they can answer.
pagesobject--
pages.check()(o) => Promise<PagesCheck>pages.checkChecks the served pages now, or (unless force) takes a result less than 10 minutes old. Resolves the check as it stands: a difference found earlier stands through a check that got no answer.
pages.now()() => PagesCheckpages.now-
settled()() => Promise<void>-Resolves once nothing this device began on opening (a refresh, an upkeep) is still under way: a test moving its clock waits for it.
sharesobject-Sharing with a contact (prd.md §7.14): a secret lent (it stays here; their devices ask, and the holder's asked each time) or given (a copy that's theirs; taking it back is a request to forget). Never a persona, records or documents.
shares.give()(secretId, contactId) => Promise<number>shares.giveGives a contact a copy of a secret kept here, as theirs: opened behind this device's gate, then offered to each of their devices to keep (their kivi asks them). Resolves how many devices it was handed to.
shares.lend()(secretId, contactId) => Promise<void>shares.lendLends a secret kept here to a contact: listed for every device of theirs; they ask, and this holder's asked each time.
shares.news()() => ShareNews[]shares.news-
shares.of()(secretId) => Promise<{ given: object[]; lent: object[]; }>shares.ofWhat's lent and given, by contact, for a secret kept here (its card).
shares.present()(o) => Promise<void>shares.presentShows a contact what a credential says, bound to them: they can check it, not use it as their own. Shows a contact what a credential says: only paths, key-bound to their device (its did:key) and a nonce it gave, so they can check it but not use it as their own. Needs their kivi open (it gives the nonce). Resolves once their kivi has checked it.
shares.presentable()() => Promise<object[]>shares.presentableWhat can be shown to a contact: each credential, by title, with the claims it can disclose one by one (their paths).
shares.stopLending()(secretId, contactId) => Promise<void>shares.stopLendingStops lending a secret to a contact: their devices are taken off its list, so nothing more reaches them.
shares.takeBack()(secretId, contactId) => Promise<void>shares.takeBackTakes back a copy given: each of the contact's devices is asked to forget it (a compliant kivi deletes it when it hears). They could have copied it first: the screens say so, and that a password given should be changed.
signInobject--
connect()(url) => Promise<SignInAsk | undefined>-Connects to the page showing url. Undefined if this device hasn't reached the holder. Rejects (and tells the page why) while the pages differ.
syncobject--
sync.otherDevices()() => Promise<CatalogueDevice[]>sync.otherDevices-
sync.refresh()() => Promise<boolean>--
walletobject--
wallet.agreedTo()(party, purpose, noticeSha256) => Promise<boolean>-Whether the holder's consent to party for purpose covers the notice with this hash, and hasn't been withdrawn since.
wallet.claims()(credential) => Claims-The minimal claims a document supports today, e.g. age_over.18, without its details.
wallet.clearUnanswered()(id) => Promise<SecretCredential>-Forgets the devices a secret was declined by or never reached: its note says only where it is, and where it's still offered.
wallet.consents()() => Promise<ConsentRecord[]>-What the holder has agreed to (and withdrawn), newest first.
wallet.credentials()() => Promise<SdJwtVcCredential & object[]>-The credentials kivi can present, derived from its documents.
wallet.deriveCredential()(doc) => Promise<SdJwtVcCredential>-Self-issues the presentable credentials for a document: the claims it supports, each selectively disclosable, with how the document was checked visible to the verifier: a small batch, each signed by and bound to its own single-use key (so its issuer is that key too), so nothing ties one presentation to another, or to the holder's identity key. kivi can always issue more of its own: the batch is topped up when it runs out.
wallet.document()(id) => Promise<DocumentCredential | undefined>--
wallet.documents()() => Promise<DocumentCredential[]>--
wallet.identity()() => Promise<IdentityKey>-The holder's did:key, created on first use.
wallet.keptAlsoOn()(id, where) => Promise<SecretCredential>-Notes that another device now keeps a copy of a secret kept (or noted) here: added as each copy is made.
wallet.markLeftTo()(id, contact, at) => Promise<SecretCredential>-Notes a secret left to the beneficiary contact (prd.md §7.11, legacy), or no longer (at undefined).
wallet.markRecoverable()(id, at) => Promise<SecretCredential>-Marks a secret recoverable (prd.md §7.11: its value is in the secrets vault), or not (at undefined).
wallet.offersDelivered()(id, deliveries) => Promise<SecretCredential>-Notes how each offer of a secret went: handed over live, left in a drop box, or not at all (custody.ts).
wallet.recordConsent()(party, purpose, notice?, detail) => Promise<ConsentRecord>-Records that the holder agreed to notice from party, for purpose (and what went, and how).
wallet.removeSecret()(id) => Promise<void>--
wallet.requestErasure()(party, fetchImpl) => Promise<{ outcome: "accepted" | "refused" | "unreachable" | "no-endpoint"; record: ConsentRecord; sent: number; }>-Asks party to erase what it received (prd.md §7.6): finds the erasure endpoint in its wellknown.id config, and sends one request per presentation made from this device, each signed with the key that bound it. Records what happened, and lets those keys go once the party has accepted.
wallet.secrets()() => Promise<SecretCredential[]>-The secrets kept here: names and kinds, never values (prd.md §7.9).
wallet.withdrawConsent()(party, purpose?, detail) => Promise<ConsentRecord>-Records that the holder withdrew their consent to party (for purpose, or everything), from now on.
addDocument()( transport, key, options? ) => Promise<DocumentCredential>-Reads a passport or ID card over transport (NFC, a card reader, the virtual passport) and keeps it. key is the MRZ details, or the card access number (CAN) printed on ID cards.
addSecret()(s, where?) => Promise<SecretCredential>-Keeps a secret: here, sealed behind this device's gate (which may ask the holder), and/or recorded as kept on bonded devices already (custody.ts sends it). Never stored anywhere unsealed.
mayAsk()( id, did, own ) => Promise<boolean>-Whether did may ask for this secret: the holder's own devices always; others only if it's shared with them.
offerAnswered()(bondId, answer) => Promise<SecretCredential | undefined>-An offered device's answer (custody.ts), from the bond it was offered over: kept (the offer becomes a copy kept there) or declined (noted, with why). Resolves the secret it was about, if any.
openSecret()(id) => Promise<Uint8Array<ArrayBufferLike>>-A secret's value: through the gate, which asks the holder each time on phones.
prepareAnswer()( requestData, origin, credentialIds?, how? ) => Promise<{ asked: object[]; protocol: string; share: (declared?) => Promise<{ vp_token: Record<string, string[]>; }>; }>--
presentTo()( credentialId, paths, aud, nonce, how? ) => Promise<string>-Presents what a credential says to a contact (prd.md §7.14), as to a site but not to one: only paths, key-bound to aud (their device's did:key) and their nonce, so they can check it but not use it as their own (anyone shown it finds the binding names someone else). Recorded (present-to-contact); a single-use credential is spent.
remove()(id) => Promise<void>--
shareSecret()(id, withDevices) => Promise<void>-Which devices of limited bonds and other people a secret is listed for (by did:key), besides the holder's own (prd.md §7.10). They see its name and kind, and may ask for it; the holder is asked each time.
witnessDocument()(doc, o) => Promise<{ count: number; witness: string; }>-Has a witness check a document's chip itself, and keeps the batch it issues, replacing any earlier batch from the same witness for this document. Each credential gets its own single-use key; the keys are made first, so the holder isn't holding the phone to the document while hardware makes them. withCard runs its argument once a card is there (NFC); connect opens the channel to the witness.
witnessesobject--
witnesses.connect()(endpoint) => Promise<WitnessConn>--
witnesses.discover()(domain) => Promise<DiscoveredWitness>-A witness's config, from its domain's wellknown.id config.
registerCredentials()() => Promise<{ registered: string[]; supported: boolean; }>-Makes this wallet's credentials available to sites through the platform (the registry port): every verified document gets its presentable credentials first (documents read before credentials existed get theirs now).