Cryptographically signs arbitrary data using the Handle's private key
(implements Signer — a Handle is acceptable as the signer
argument of Attestation.issue).
The data to be signed, as a Uint8Array.
A Promise resolving to the Ed25519 signature as a Uint8Array.
StaticverifyVerifies an Ed25519 signature against the provided data and public key.
A signature whose length differs from the 64-byte Ed25519 size is
rejected with false before any crypto runs (PR-7 pre-check) — the
method therefore never throws for short/long signatures.
The signature to verify (Uint8Array).
The original data that was signed (Uint8Array).
The public key to verify against (Uint8Array).
A Promise resolving to true if the signature is valid, false otherwise.
A deep copy of the metadata object associated with this
Handle — mutating the returned object does not affect the Handle.
Custom metadata is deep-copied (JSON-safe structures up to depth 8);
non-plain values (Date, Map, functions) remain shared by reference —
same caveats as getSubMetadata().
Derives a SubHandle from this Handle.
This is the autonomous entry point for IoT devices and other contexts where the Identity is not available locally. The Handle uses its own private key internally (it never leaves the class) to derive a child key via HKDF-SHA256.
The resulting SubHandle is cryptographically identical to the one produced
by Identity.deriveSubHandle(this.name, subName).
The SubHandle name (will be normalized).
Optionalmetadata: SubHandleMetadataOptional SubHandle metadata with constraints.
A Promise resolving to the derived SubHandle with path [this.name, name].
Issues an attestation for a derived SubHandle, binding its derived key to the name and grant. Autonomous: requires only this Handle's own key, no Identity and no network.
The child key is derived internally, so the attestation's
subjectId always equals the key produced by
identity.deriveSubHandle(this.name, subName).
Note: this method does not check this Handle's own grant — it may
not have one (Mode 1). Nesting is enforced by verifiers in
Session.verifyAttested (a child grant exceeding the parent's is
rejected with SCOPE_EXCEEDED/TTL_EXCEEDED at level ROOT).
Optionalopts: { OptionalttlOptionalexpiresOptionaljti?: stringOptionalnow?: numberDeterministically derives a secret (e.g., a password or API key) for a specific service context.
The private key never leaves this class, ensuring maximum security.
The context is canonicalized NFKC → lowercase → trim before use
(' GitHub ', 'github' and 'GitHub' yield the same secret).
NFKC applies to the string context only — options.revision is
an integer and is canonical by construction (no normalization).
Revision model (since 0.8.0, D3): options.revision (positive
integer, default 1) selects an independent secret for the same
context — revision 1 is the initial secret, revision 2, 3, … are for
rotation (e.g. after a breach). Revision 1 is byte-identical to the
pre-revision v1.0 derivation.
A unique identifier for the service (e.g., 'google', 'github').
Derivation options.
A URL-safe base64 string suitable for use as a strong password.
Derives a symmetric 256-bit channel key for secure communication between the Identity (controller) and this Handle (device/context).
Both parties can independently compute this key because:
identity.deriveHandle(name).This enables zero-knowledge encrypted channels without key exchange protocols.
The context is canonicalized NFKC → lowercase → trim before use.
NFKC applies to the string context only — options.revision is
an integer and is canonical by construction (no normalization).
Revision model (since 0.8.0, D3): options.revision (positive
integer, default 1) selects an independent 32-byte key for the same
context — the sanctioned rotation path for a compromised channel
without changing the context string. Revision 1 is byte-identical to
the pre-revision v1.0 derivation.
Channel identifier for domain separation (e.g., 'drone-001').
Both parties MUST use the same context and the same revision.
Derivation options.
A 32-byte Uint8Array suitable for AES-256-GCM encryption.
Derives a shared secret using ECDH (X25519) for P2P key exchange.
Converts the Ed25519 keypair to X25519 for Diffie-Hellman key agreement, then applies HKDF-SHA256 to produce a clean 32-byte channel key.
The Ed25519 public key of the other party (32 bytes).
A Promise resolving to a 32-byte shared secret suitable for AES-256-GCM.
Permanently destroys this Handle's key material.
The private key bytes are zeroized; afterwards every capability method (sign, deriveSubHandle, attestSubHandle, derivePassword, deriveChannelKey, deriveSharedSecret) throws a DisposedError. Informational getters (getName, getId, getPublicKey, getMetadata) keep working — they never touch key material.
SubHandles inherit dispose(). Calling it more than once is a no-op
(idempotent).
Redacted serialization contract: JSON.stringify(handle) exposes only
the object kind, the public identifier and the context name — never
key material or metadata.
Represents a context-specific cryptographic handle derived from an Identity.
A Handle encapsulates a private key tied to a specific name (context). It provides methods for signing data, verifying signatures, and deriving secondary secrets (passwords, channel keys, shared secrets) without ever exposing the underlying private key.
A Handle can also derive SubHandles for hierarchical access control (e.g., an IoT device deriving keys for its components). The private key never leaves the Handle — derivation is performed internally using HKDF.
Handles own their lifecycle: Handle.dispose zeroizes the private key, after which every capability method throws a DisposedError. Serialization (
JSON.stringify, console inspection) is redacted and never emits key material.