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
_apskbefore anything is sealed to it, but the atServer serves the_apsktoo — 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.persistenceandpublicKeyCacheSettingstake 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
rsa2048otherwise — 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:
cacheExpiryfrom 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
apskEntriesand spelled byapskValueOf.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
__ssenvkey, 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
kidnames, 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
enrollmentIdofatSignhas 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
secretto 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.excludeEnrollmentIdsdrops 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 exactnamesand/or anamePrefix(e.g.__rk.for epoch keys).excludeEnrollmentIdsdrops 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
payloadtoto's key package and stores it for delivery, addressed throughappNamespace.inherited -
Shares every secret in secretStore with
to, filtered — whenapprovedNamespacesis given — to secrets whose namespace that enrollment is authorized for (SecretStore.namespaceAuthorizes).inherited -
For use by an enrollment approver, after approving
enrollmentIdwithapprovedNamespaces(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 -
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.keyPackagewhen the package ridesenroll: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
payloadin 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