AtClientSecretSharing class

Ready-made composition of the same-atSign secret-sharing mixins, for apps that don't want to mix them into their own classes.

final sharing = AtClientSecretSharing.forClient(atClient);
await sharing.register();
await sharing.startListening();
sharing.receivedSecrets.listen((r) => ...);

Prefer AtClientSecretSharing.forClient over the plain constructor: a secret-sharing instance is this APKAM keypair's recipient identity (its KEM enc keypair, registered key package, and envelope listener) plus its SecretStore. Reusing one instance per AtClient lets every consumer (the app and SDK-internal ones, e.g. the namespace-key provider, which conveys its privates as secrets) share a single store and a single registration rather than minting duplicate enc keypairs. (Correctness does not depend on it — kpid-addressed envelopes converge idempotently via SecretStore.putIfNewer — but a single instance avoids redundant registration and double delivery.)

A recipient key package is APKAM-signed by the enrollment that generated it and verified against that enrollment's _apsk before anything is sealed to it, but the atServer serves the _apsk too — so the operator of the atSign's own atServer is still inside the confidentiality boundary. Durable storage of received secrets is an app-supplied hook.

Mixed-in types

Constructors

AtClientSecretSharing(AtClient atClient, {({Duration cacheExpiry})? publicKeyCacheSettings = (cacheExpiry: Duration(minutes: 5))})
Direct construction creates an independent instance with its own enc keypair. Use forClient unless that is what you want (tests, custom compositions). PairwiseSecretSharing.perEnrollmentSecretRequestGate starts null — fail closed — until a composition installs a resolver.
AtClientSecretSharing.forClient(AtClient atClient, {SecretStorePersistence? persistence, ({Duration cacheExpiry})? publicKeyCacheSettings = (cacheExpiry: Duration(minutes: 5))})
The shared secret-sharing instance for atClient, created on first call. persistence and publicKeyCacheSettings take effect only on the creating call; later calls return the cached instance unchanged (one SecretStorePersistence per APKAM keypair — see SecretStore.persistence).
factory

Properties

answerSecretRequests ↔ SecretRequestPolicy?
Optional app override for the kind:'request' answer decision. Null = the default policy (answer any requester that resolves to an authorized key package of the request's namespace).
getter/setter pairinherited
atClient → AtClient
final
authenticationSigningKey → Future<ApkamSigningKeys?>
The APKAM authentication keypair as a signing key: the keyfile's for enrollmentId — typed material first, the flat pair as rsa2048 otherwise — or null when it holds neither.
no setterinherited
clientRunsSync ↔ bool
Whether this client runs sync (default true).
getter/setter pairinherited
configuredKeyAlgo → String
The KEM this client would mint a fresh key under — the primary, first of AtClientPreference.keyEstablishmentAlgorithms.
no setterinherited
directory ↔ EnrollmentDirectory
The atServer-backed key-package directory. Defaults to a verb-backed implementation; tests assign a fake.
getter/setter pairinherited
encKeyAlgo → String
Which KEM encPublicKey belongs to — an id from SecretSharingAlgos.keyAlgos. Throws StateError until register completes.
no setterinherited
encPublicKey → Uint8List
This APKAM keypair's encapsulation public key (raw bytes) — the active one. Throws StateError until register has completed.
no setterinherited
enrollmentId → String
no setterinherited
envelopeTtl ↔ Duration
How long an unconsumed envelope lives on the atServer.
getter/setter pairinherited
fileReceivedSecret ↔ Future<void> Function(Secret secret)?
Files a secret that just arrived durably, before the envelope carrying it is deleted; a throw keeps the envelope, which is handled again at the next start.
getter/setter pairinherited
hashCode → int
The hash code for this object.
no setterinherited
heldApkamKeys → PersistedApkamKeys
The KEM enc keys this APKAM keypair holds, in the shape saveApkamKeys receives; empty until register.
no setterinherited
heldKpids → Set<String>
Every address this client can be reached at — the active key's and every retired key's.
no setterinherited
heldSigningKeys → Future<List<ApkamSigningKeys>>
What the keyfile holds for enrollmentId, filtered to what this build can sign an envelope with. Empty when the client has no key source or no keyfile, or when the enrollment holds none; a keyfile that cannot be read throws.
no setterinherited
heldStatuses → Map<String, KeyEntryStatus>
The status of every key heldKpids names, by kid.
no setterinherited
isRegistered → bool
no setterinherited
kpid → String
This key package's addressing id (KeyPackage.kpid) — the SHA-256 prefix of the active encapsulation public key. Throws StateError until register completes.
no setterinherited
loadApkamKeys ↔ Future<PersistedApkamKeys?> Function()?
Supply to give this APKAM keypair a stable enc keypair across restarts. Called once, before generating a fresh one; return null to generate fresh.
getter/setter pairinherited
logger → AtSignLogger
final
myKeyPackage → KeyPackage
This APKAM keypair's key package (the one registered for discovery). Throws StateError until register has generated the enc keypair.
no setterinherited
perEnrollmentSecretRequestGate ↔ Future<bool> Function(String requesterEnrollmentId)?
Decides whether the enrollment behind a request may be handed a per-enrollment (__en.-prefixed) secret.
getter/setter pairinherited
pubKeyCache → Map<String, (String, DateTime)>
Cached public keys and when each expires. An entry is dropped when a lookup finds it expired, and on each insert, so nothing here holds a timer.
no setterinherited
publicKeyCacheSettings → ({Duration cacheExpiry})?
How long a public key fetched for verification is used before it is fetched again: cacheExpiry from the fetch, however often it is used.
final
publicSigningKeyUri → String
Where this enrollment's signing keys are advertised, e.g. public:_apsk.<enrollment_id>.a.__e@atsign. The record holds publicSigningKeyValue — every key, not one.
no setterinherited
publicSigningKeyValue → Future<String>
What publishPublicSigningKey writes: what signs for this enrollment now plus the signing keys it has withdrawn from service, composed by apskEntries and spelled by apskValueOf.
no setterinherited
receivedEnvelopes → Stream<ReceivedEnvelope>
Decrypted, verified payloads addressed to this client. Listen, then call startListening.
no setterinherited
receivedSecrets → Stream<ReceivedSecret>
Secrets shared with this client by other APKAM keypairs (already verified, decrypted, and stored in secretStore). Listen, then call startListening.
no setterinherited
requestAnswerJitter ↔ Duration
How long a responder waits, uniformly at random within this window, before answering a pull request.
getter/setter pairinherited
requestAnswerMinInterval ↔ Duration
Anti-storm floor: the same (requester, secret-name) is answered at most once per this interval. A burst of duplicate requests collapses to one share.
getter/setter pairinherited
requestAnswerRandom ↔ Random
Source of the jitter, injectable so a test can make it deterministic.
getter/setter pairinherited
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
saveApkamKeys ↔ Future<void> Function(PersistedApkamKeys keys)?
Supply to persist a freshly generated enc keypair (e.g. to the keyfile / platform keystore — the app's concern).
getter/setter pairinherited
secretStore → SecretStore
The secrets this client holds: what it created via SecretStore.putSecret, plus what other APKAM keypairs shared with it (received secrets are stored automatically, newest wins).
finalinherited
sendWakeUpNotification ↔ bool
Whether sendEnvelope also fires a best-effort wake-up notification (default on) after the put. Clients that run sync receive envelopes via sync without it; sync-less clients rely on it (their startListening monitors for it and does a remote sweep). It is best-effort: the envelope is already on the atServer when this fires, so a failed wake-up never fails the send and a wake-up that arrives never points at a value that has not landed. A future atServer enhancement will emit this notification itself on a put to an __ssenv key, at which point senders can leave it off.
getter/setter pairinherited
signingKeys → Future<List<ApkamSigningKeys>>
The keys this client signs content with, strongest algorithm first: those of its client data signing keys that have a link, while any do, and otherwise every one it holds. Never empty: throws AtKeyNotFoundException when it holds no key to sign with, as when it has no keyfile, and a keyfile that cannot be read throws that read's error.
no setterinherited
sweepInterval ↔ Duration
How often startListening sweeps for envelopes, in addition to sweeping when sync delivers one. Whether that sweep reads the local store or the atServer is decided by clientRunsSync.
getter/setter pairinherited
withdrawnSigningKeys → Future<List<({SigningAlgoType algorithm, String publicKey, KeyEntryStatus status})>>
The public half of every signing key this enrollment has taken out of service, each with the status the keyfile gives it — the non-active entries of its advertisement, which keep envelopes signed before the key was withdrawn verifiable.
no setterinherited

Methods

attachToServices() → void
Hooks the running listener onto whichever of the client's sync and notification services are set, once each, so a service set after startListening is attached too; a no-op before it and after stopListening.
inherited
cachePubKey(String atSign, String enrollmentId, String pubKey) → void
inherited
detachFromServices() → void
Unhooks the running listener from the services it is attached to, so the next attachToServices hooks it onto the client's current ones.
inherited
encKeyFor(String kid) → ({String keyAlgo, Uint8List secretKey})?
The decapsulation key for the key kid names, and which KEM it belongs to — for opening by composing mixins, not for application use. Null if this client holds no such key.
inherited
getApkamPublicKey(String atSign, String enrollmentId) → Future<String>
Fetch the APKAM public signing key which enrollmentId of atSign has published in its per-enrollment namespace. See ApkamSigning.publicSigningKeyUri.
inherited
lookupPubKey(String atSign, String enrollmentId) → String?
inherited
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
publishPublicSigningKey({String? value}) → Future
Publishes this client's signing keys at publicSigningKeyUri, if what is there is not already what it holds.
inherited
publishPublicSigningKeyLocked({String? value}) → Future
publishPublicSigningKey without taking the lock, for a caller that already holds it. Taking it twice from one call chain would deadlock: the second acquire waits on a chain entry only the first can complete.
inherited
pushSecretToNamespaceMembers(Secret secret, {Set<String> excludeEnrollmentIds = const {}}) → Future<int>
Pushes one secret to every key package registered for its namespace (minus this client). This is the mint/rotation push: it enumerates the namespace's members via the directory, seals once per key package, and puts the kpid-addressed envelope. excludeEnrollmentIds drops revoked enrollments. Returns the number of key packages it was pushed to.
inherited
register() → Future<KeyPackage>
Generates (or loads, via loadApkamKeys) this APKAM keypair's KEM enc keypair and publishes its APKAM signing key (so peers can verify its envelopes), then returns this client's KeyPackage. Idempotent.
inherited
reloadApkamKeys() → Future<void>
Reloads the held keys and, when listening, listens at the addresses they now give.
inherited
requestSecret(String namespace, String name, {Duration timeout = const Duration(seconds: 30), Set<String> excludeEnrollmentIds = const {}}) → Future<Secret>
Requests a single named secret from the holders in namespace, then resolves when it arrives (waitForSecret).
inherited
requestSecretsFromNamespace(String namespace, {List<String>? names, String? namePrefix, Set<String> excludeEnrollmentIds = const {}}) → Future<int>
Broadcasts a pull request for held secrets to every key package registered for namespace (minus this client). Holders that pass the answer policy reply by sharing the matching secrets; the caller typically then waitForSecrets. Filter the request by exact names and/or a namePrefix (e.g. __rk. for epoch keys). excludeEnrollmentIds drops revoked enrollments. Returns the number of key packages the request was sent to.
inherited
sendEnvelope(KeyPackage to, String appNamespace, Map<String, dynamic> payload, {String inReplyTo = EnvelopeAddressing.unsolicited}) → Future<void>
Encrypts payload to to's key package and stores it for delivery, addressed through appNamespace.
inherited
shareAllSecretsWith(KeyPackage to, {Map<String, dynamic>? approvedNamespaces, bool senderMayWrite(String namespace)?, Set<String>? excludeEnrollmentIds}) → Future<int>
Shares every secret in secretStore with to, filtered — when approvedNamespaces is given — to secrets whose namespace that enrollment is authorized for (SecretStore.namespaceAuthorizes).
inherited
shareAllSecretsWithEnrollment(String enrollmentId, Map<String, dynamic> approvedNamespaces, {Set<String>? excludeEnrollmentIds}) → Future<int>
For use by an enrollment approver, after approving enrollmentId with approvedNamespaces (both available from the enrollment request): shares with each of the newly-approved enrollment's key packages every held secret the enrollment's namespaces authorize.
inherited
shareSecretWith(KeyPackage to, Secret secret, {required String inReplyTo}) → Future<void>
Shares one secret with one key package.
inherited
signedKeyPackagePayload() → Future<SignedEnvelope>
This key package wrapped in an APKAM-signed envelope — the value to store at metadata.keyPackage when the package rides enroll:request.
inherited
startListening() → Future<void>
Starts watching for envelopes addressed to this client: sweeps now, after every sync that delivers an envelope key, and every sweepInterval; and subscribes to wake-up notifications, doing a remote sweep on each.
inherited
stop() → void
Stops for good, when the client does: listening ends, a waitForSecret still waiting fails with StoppedException, and a sweep in flight ends at its next envelope.
inherited
stopListening() → void
Stops watching. The receivedEnvelopes stream stays open; a later startListening resumes.
inherited
sweepOnce({bool fromRemote = false}) → Future<int>
Scans for envelopes addressed to this client; verifies, decrypts, emits and deletes each. Returns how many envelopes were consumed. Safe to call concurrently with the periodic sweep, a sync-triggered sweep, and a wake-up sweep — each envelope key is claimed synchronously before any await, so the same payload is never emitted twice.
inherited
toString() → String
A string representation of this object.
inherited
verifyEnvelopeSignature(SignedEnvelope envelope, {required String signerAtSign, String? signerEnrollmentId, EnvelopeType expecting = EnvelopeType.app}) → Future<void>
Verify an envelope created by wrapAndSign or wrapAndSignAndJsonEncode.
inherited
waitForSecret(String namespace, String name, {Duration timeout = const Duration(seconds: 30)}) → Future<Secret>
Returns the secret (namespace, name) as soon as this client holds it: immediately from secretStore when already present, otherwise the first matching arrival on receivedSecrets — subscription is set up before the store check, so an arrival between the two cannot be missed. startListening must be active for arrivals to be observed.
inherited
wrapAndSign(Object? payload, {Object? toEncodable(Object? nonEncodable)?, EnvelopeType type = EnvelopeType.app}) → Future<SignedEnvelope>
Create a json envelope around payload in a format that can be verified by verifyEnvelopeSignature.
inherited
wrapAndSignAndJsonEncode(Object? payload, {Object? toEncodable(Object? nonEncodable)?, EnvelopeType type = EnvelopeType.app}) → FutureOr<String>
Same as wrapAndSign but we also call jsonEncode for you :)
inherited

Operators

operator ==(Object other) → bool
The equality operator.
inherited