@kubun/credential 0.17.4 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api.d.ts +61 -30
- package/lib/api.js +455 -111
- package/lib/authority.d.ts +4 -0
- package/lib/authority.js +3 -0
- package/lib/branch.d.ts +4 -0
- package/lib/branch.js +24 -0
- package/lib/builder.d.ts +5 -0
- package/lib/builder.js +8 -6
- package/lib/canonical-token.d.ts +2 -0
- package/lib/canonical-token.js +10 -0
- package/lib/errors.d.ts +39 -1
- package/lib/errors.js +77 -0
- package/lib/fork-repair.d.ts +19 -0
- package/lib/fork-repair.js +3 -0
- package/lib/grantor-authority.d.ts +26 -0
- package/lib/grantor-authority.js +43 -0
- package/lib/index.d.ts +15 -6
- package/lib/index.js +7 -2
- package/lib/key-cache.d.ts +4 -3
- package/lib/key-cache.js +22 -15
- package/lib/manager-context.d.ts +50 -0
- package/lib/manager-context.js +82 -0
- package/lib/manager-entries.d.ts +3 -0
- package/lib/manager-entries.js +585 -0
- package/lib/manager-keys.d.ts +3 -0
- package/lib/manager-keys.js +413 -0
- package/lib/manager-open.d.ts +45 -0
- package/lib/manager-open.js +421 -0
- package/lib/manager-records.d.ts +94 -0
- package/lib/manager-records.js +291 -0
- package/lib/manager-removal-recovery.d.ts +7 -0
- package/lib/manager-removal-recovery.js +211 -0
- package/lib/manager-rotate.d.ts +15 -0
- package/lib/manager-rotate.js +536 -0
- package/lib/manager-types.d.ts +337 -0
- package/lib/manager-types.js +1 -0
- package/lib/manager.d.ts +2 -293
- package/lib/manager.js +38 -1073
- package/lib/manifest-token.d.ts +31 -0
- package/lib/manifest-token.js +51 -0
- package/lib/provider.d.ts +15 -2
- package/lib/provider.js +39 -0
- package/lib/record-types.d.ts +65 -0
- package/lib/record-types.js +865 -0
- package/lib/refreshing-credential-provider.d.ts +6 -2
- package/lib/refreshing-credential-provider.js +76 -13
- package/lib/suite.d.ts +5 -4
- package/lib/suite.js +2 -2
- package/package.json +16 -15
- package/lib/op.d.ts +0 -128
- package/lib/op.js +0 -304
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { OAuthProviderDefinition } from '@kokuin/oauth';
|
|
2
|
-
import type { AliasBindingCredentialProvider, Credential, CredentialProvider, EntryPairedCredentialProvider } from '@kubun/credential-types';
|
|
2
|
+
import type { AliasBindingCredentialProvider, Credential, CredentialProvider, EntryPairedCredentialProvider, GrantAddress, VersionedEntryCredentialProvider } from '@kubun/credential-types';
|
|
3
3
|
import type { Logger } from '@kubun/logger';
|
|
4
4
|
import { EventEmitter } from '@sozai/event';
|
|
5
5
|
import type { Runtime } from '@sozai/runtime';
|
|
@@ -22,18 +22,20 @@ export type RefreshEvents = {
|
|
|
22
22
|
export type RefreshOutcome = {
|
|
23
23
|
ok: true;
|
|
24
24
|
credential: Credential;
|
|
25
|
+
superseded?: boolean;
|
|
25
26
|
} | {
|
|
26
27
|
ok: false;
|
|
27
28
|
category: RefreshFailureCategory;
|
|
28
29
|
};
|
|
29
30
|
export type RefreshingCredentialProviderParams = {
|
|
30
|
-
inner:
|
|
31
|
+
inner: InnerCredentialProvider;
|
|
31
32
|
runtime: Runtime;
|
|
32
33
|
providers: Array<OAuthProviderDefinition>;
|
|
33
34
|
bufferSeconds?: number;
|
|
34
35
|
logger?: Logger;
|
|
35
36
|
refreshInFlight?: Map<string, Promise<RefreshOutcome>>;
|
|
36
37
|
};
|
|
38
|
+
type InnerCredentialProvider = CredentialProvider & EntryPairedCredentialProvider & AliasBindingCredentialProvider & Partial<VersionedEntryCredentialProvider>;
|
|
37
39
|
export declare class RefreshingCredentialProvider implements CredentialProvider, AliasBindingCredentialProvider {
|
|
38
40
|
#private;
|
|
39
41
|
constructor(params: RefreshingCredentialProviderParams);
|
|
@@ -47,6 +49,8 @@ export declare class RefreshingCredentialProvider implements CredentialProvider,
|
|
|
47
49
|
ownerWrappableDID?: string;
|
|
48
50
|
controllerWrappableDID?: string;
|
|
49
51
|
}): Promise<void>;
|
|
52
|
+
update(address: GrantAddress, change: (current: Credential | null) => Credential): Promise<void>;
|
|
50
53
|
delete(providerName: string, ownerDID: string): Promise<void>;
|
|
51
54
|
bindAlias(providerName: string, ownerDID: string, entryID: string): Promise<void>;
|
|
52
55
|
}
|
|
56
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { OAuthTokenError, refreshToken as requestRefreshedToken } from '@kokuin/oauth';
|
|
2
2
|
import { normalizeDID } from '@kokuin/token';
|
|
3
|
+
import { CredentialUpdateUnavailableError } from '@kubun/credential-types';
|
|
3
4
|
import { EventEmitter } from '@sozai/event';
|
|
4
5
|
const DEFAULT_BUFFER_SECONDS = 300;
|
|
5
6
|
// Wraps a storage-only `CredentialProvider` and adds transparent OAuth refresh on
|
|
@@ -38,11 +39,11 @@ export class RefreshingCredentialProvider {
|
|
|
38
39
|
return this.#inner.get(providerName, ownerDID);
|
|
39
40
|
}
|
|
40
41
|
async get(providerName, ownerDID) {
|
|
41
|
-
const ref = await this.#
|
|
42
|
+
const ref = await this.#readRef(providerName, ownerDID);
|
|
42
43
|
if (ref == null) return null;
|
|
43
|
-
const { credential,
|
|
44
|
+
const { credential, entry } = ref;
|
|
44
45
|
if (this.#isExpiringSoon(credential) && credential.refreshToken != null) {
|
|
45
|
-
const refreshed = await this.#refresh(providerName, ownerDID,
|
|
46
|
+
const refreshed = await this.#refresh(providerName, ownerDID, entry, credential, credential.refreshToken);
|
|
46
47
|
return refreshed ?? credential;
|
|
47
48
|
}
|
|
48
49
|
return credential;
|
|
@@ -50,6 +51,10 @@ export class RefreshingCredentialProvider {
|
|
|
50
51
|
set(params) {
|
|
51
52
|
return this.#inner.set(params);
|
|
52
53
|
}
|
|
54
|
+
update(address, change) {
|
|
55
|
+
if (this.#inner.update == null) throw new CredentialUpdateUnavailableError();
|
|
56
|
+
return this.#inner.update(address, change);
|
|
57
|
+
}
|
|
53
58
|
delete(providerName, ownerDID) {
|
|
54
59
|
return this.#inner.delete(providerName, ownerDID);
|
|
55
60
|
}
|
|
@@ -59,6 +64,28 @@ export class RefreshingCredentialProvider {
|
|
|
59
64
|
bindAlias(providerName, ownerDID, entryID) {
|
|
60
65
|
return this.#inner.bindAlias(providerName, ownerDID, entryID);
|
|
61
66
|
}
|
|
67
|
+
async #readRef(providerName, ownerDID) {
|
|
68
|
+
if (this.#inner.getVersionedCredentialRef != null) {
|
|
69
|
+
const ref = await this.#inner.getVersionedCredentialRef(providerName, ownerDID);
|
|
70
|
+
if (ref == null) return null;
|
|
71
|
+
return {
|
|
72
|
+
credential: ref.credential,
|
|
73
|
+
entry: {
|
|
74
|
+
entryID: ref.entryID,
|
|
75
|
+
version: ref.version
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const ref = await this.#inner.getCredentialRef(providerName, ownerDID);
|
|
80
|
+
if (ref == null) return null;
|
|
81
|
+
return {
|
|
82
|
+
credential: ref.credential,
|
|
83
|
+
entry: {
|
|
84
|
+
entryID: ref.entryID,
|
|
85
|
+
version: undefined
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
62
89
|
#isExpiringSoon(credential) {
|
|
63
90
|
if (credential.expiresAt == null) return false;
|
|
64
91
|
return credential.expiresAt.getTime() - Date.now() < this.#bufferMs;
|
|
@@ -74,18 +101,18 @@ export class RefreshingCredentialProvider {
|
|
|
74
101
|
// `get`). Followers await the coordinator's promise and reuse its outcome for
|
|
75
102
|
// their own stale-fallback decision without emitting a second event. The key is
|
|
76
103
|
// normalized so the two `did:peer:4` spellings of one owner share an entry.
|
|
77
|
-
async #refresh(providerName, ownerDID,
|
|
104
|
+
async #refresh(providerName, ownerDID, entry, credential, refreshToken) {
|
|
78
105
|
// entryID is part of the key: a caller that read a DIFFERENT entry (a
|
|
79
106
|
// delete+rebind landed between its read and this one) must never join an
|
|
80
107
|
// in-flight exchange started against the old entry and receive its
|
|
81
108
|
// refreshed credential.
|
|
82
|
-
const key = `${providerName}:${normalizeDID(ownerDID)}:${entryID}`;
|
|
109
|
+
const key = `${providerName}:${normalizeDID(ownerDID)}:${entry.entryID}`;
|
|
83
110
|
const inFlight = this.#refreshInFlight.get(key);
|
|
84
111
|
if (inFlight != null) {
|
|
85
112
|
const outcome = await inFlight;
|
|
86
113
|
return outcome.ok ? outcome.credential : null;
|
|
87
114
|
}
|
|
88
|
-
const exchange = this.#exchange(providerName, ownerDID,
|
|
115
|
+
const exchange = this.#exchange(providerName, ownerDID, entry, credential, refreshToken);
|
|
89
116
|
this.#refreshInFlight.set(key, exchange);
|
|
90
117
|
let outcome;
|
|
91
118
|
try {
|
|
@@ -94,6 +121,9 @@ export class RefreshingCredentialProvider {
|
|
|
94
121
|
this.#refreshInFlight.delete(key);
|
|
95
122
|
}
|
|
96
123
|
if (outcome.ok) {
|
|
124
|
+
// A superseded exchange refreshed nothing that was kept: whoever replaced
|
|
125
|
+
// the stored credential reports its own outcome.
|
|
126
|
+
if (outcome.superseded) return outcome.credential;
|
|
97
127
|
this.#events.fire('refreshed', {
|
|
98
128
|
providerName,
|
|
99
129
|
ownerDID,
|
|
@@ -113,7 +143,7 @@ export class RefreshingCredentialProvider {
|
|
|
113
143
|
// one provider+owner never reach here twice: `#refresh` shares a single pending
|
|
114
144
|
// `#exchange` promise through the single-flight map. Returns a discriminated
|
|
115
145
|
// outcome and does not emit or fall back itself.
|
|
116
|
-
async #exchange(providerName, ownerDID,
|
|
146
|
+
async #exchange(providerName, ownerDID, entry, credential, refreshToken) {
|
|
117
147
|
const provider = this.#providers.find((p)=>p.name === providerName);
|
|
118
148
|
if (provider == null) {
|
|
119
149
|
return {
|
|
@@ -151,12 +181,7 @@ export class RefreshingCredentialProvider {
|
|
|
151
181
|
};
|
|
152
182
|
let write;
|
|
153
183
|
try {
|
|
154
|
-
write = await this.#
|
|
155
|
-
providerName,
|
|
156
|
-
ownerDID,
|
|
157
|
-
entryID,
|
|
158
|
-
credential: refreshed
|
|
159
|
-
});
|
|
184
|
+
write = await this.#writeBack(providerName, ownerDID, entry, refreshed);
|
|
160
185
|
} catch {
|
|
161
186
|
// A rotated refresh token (the endpoint issued a new one) means the prior
|
|
162
187
|
// token may already be invalidated server-side, so a lost write here
|
|
@@ -180,9 +205,47 @@ export class RefreshingCredentialProvider {
|
|
|
180
205
|
category: 'stale'
|
|
181
206
|
};
|
|
182
207
|
}
|
|
208
|
+
// The entry was written since this refresh read it (a consent landed
|
|
209
|
+
// mid-exchange): that write was kept, so hand back what is stored now. A
|
|
210
|
+
// failed reread falls back to the credential this refresh started from.
|
|
211
|
+
if (write === 'superseded') {
|
|
212
|
+
let current = null;
|
|
213
|
+
try {
|
|
214
|
+
current = (await this.#readRef(providerName, ownerDID))?.credential ?? null;
|
|
215
|
+
} catch {
|
|
216
|
+
// Fall back below: the write-back itself succeeded as a no-op.
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
ok: true,
|
|
220
|
+
credential: current ?? credential,
|
|
221
|
+
superseded: true
|
|
222
|
+
};
|
|
223
|
+
}
|
|
183
224
|
return {
|
|
184
225
|
ok: true,
|
|
185
226
|
credential: refreshed
|
|
186
227
|
};
|
|
187
228
|
}
|
|
229
|
+
// Writes a refreshed credential back to the exact entry the refresh read. With
|
|
230
|
+
// a version it is conditional too: the store checks and writes in one call, so
|
|
231
|
+
// a consent that replaced the entry mid-exchange is never overwritten and no
|
|
232
|
+
// lock has to span the exchange or the consent's transaction.
|
|
233
|
+
#writeBack(providerName, ownerDID, entry, credential) {
|
|
234
|
+
const { entryID, version } = entry;
|
|
235
|
+
if (version != null && this.#inner.setCredentialForEntryIfUnchanged != null) {
|
|
236
|
+
return this.#inner.setCredentialForEntryIfUnchanged({
|
|
237
|
+
providerName,
|
|
238
|
+
ownerDID,
|
|
239
|
+
entryID,
|
|
240
|
+
credential,
|
|
241
|
+
expectedVersion: version
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return this.#inner.setCredentialForEntry({
|
|
245
|
+
providerName,
|
|
246
|
+
ownerDID,
|
|
247
|
+
entryID,
|
|
248
|
+
credential
|
|
249
|
+
});
|
|
250
|
+
}
|
|
188
251
|
}
|
package/lib/suite.d.ts
CHANGED
|
@@ -44,10 +44,7 @@ export type CredentialSuite = {
|
|
|
44
44
|
generateContentKey(runtime: Runtime): Uint8Array;
|
|
45
45
|
wrap(params: WrapParams): Promise<WrapResult>;
|
|
46
46
|
unwrap(params: UnwrapParams): Promise<Uint8Array>;
|
|
47
|
-
encryptEntry(params: EncryptEntryParams):
|
|
48
|
-
iv: Uint8Array;
|
|
49
|
-
ciphertext: Uint8Array;
|
|
50
|
-
};
|
|
47
|
+
encryptEntry(params: EncryptEntryParams): EncryptEntryResult;
|
|
51
48
|
decryptEntry(params: DecryptEntryParams): Uint8Array;
|
|
52
49
|
};
|
|
53
50
|
export type WrapParams = {
|
|
@@ -64,6 +61,10 @@ export type WrapParams = {
|
|
|
64
61
|
*/
|
|
65
62
|
methods?: MethodRegistry;
|
|
66
63
|
};
|
|
64
|
+
export type EncryptEntryResult = {
|
|
65
|
+
iv: Uint8Array;
|
|
66
|
+
ciphertext: Uint8Array;
|
|
67
|
+
};
|
|
67
68
|
export type WrapResult = {
|
|
68
69
|
descriptors: Array<CredentialFactorDescriptor>;
|
|
69
70
|
iv: Uint8Array;
|
package/lib/suite.js
CHANGED
|
@@ -239,8 +239,8 @@ const suite1 = {
|
|
|
239
239
|
};
|
|
240
240
|
},
|
|
241
241
|
async unwrap (params) {
|
|
242
|
-
// Validated on open too, not only on create: wrappings arrive
|
|
243
|
-
//
|
|
242
|
+
// Validated on open too, not only on create: wrappings arrive from peers,
|
|
243
|
+
// and a pin-only wrapping is openable by anyone who
|
|
244
244
|
// guesses four digits, a factorless one by anyone at all. A wrapping this
|
|
245
245
|
// build refuses to make is not one it agrees to open.
|
|
246
246
|
validateFactorList(params.descriptors.map((descriptor)=>descriptor.kind));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubun/credential",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"license": "see LICENSE.md",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -17,25 +17,26 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@kokuin/jwe": "^0.1.0",
|
|
19
19
|
"@kokuin/oauth": "^0.1.1",
|
|
20
|
-
"@kokuin/token": "^0.5.
|
|
21
|
-
"@kubun/credential-types": "^0.
|
|
22
|
-
"@kubun/db": "^0.
|
|
23
|
-
"@kubun/hlc": "^0.
|
|
24
|
-
"@kubun/id": "^0.
|
|
25
|
-
"@kubun/logger": "^0.
|
|
26
|
-
"@kubun/
|
|
20
|
+
"@kokuin/token": "^0.5.1",
|
|
21
|
+
"@kubun/credential-types": "^0.18.0",
|
|
22
|
+
"@kubun/db": "^0.18.0",
|
|
23
|
+
"@kubun/hlc": "^0.18.0",
|
|
24
|
+
"@kubun/id": "^0.18.0",
|
|
25
|
+
"@kubun/logger": "^0.18.0",
|
|
26
|
+
"@kubun/records": "^0.18.0",
|
|
27
|
+
"@kubun/store-credential": "^0.18.0",
|
|
27
28
|
"@noble/ciphers": "^2.4.0",
|
|
28
29
|
"@noble/hashes": "^2.4.0",
|
|
29
|
-
"@sozai/codec": "^0.4.
|
|
30
|
-
"@sozai/event": "^0.1.
|
|
30
|
+
"@sozai/codec": "^0.4.3",
|
|
31
|
+
"@sozai/event": "^0.1.6",
|
|
31
32
|
"@sozai/runtime": "^0.1.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"@kubun/db-adapter": "^0.
|
|
35
|
-
"@kubun/db-better-sqlite": "^0.
|
|
36
|
-
"@kubun/db-postgres": "^0.
|
|
37
|
-
"@testcontainers/postgresql": "^12.1
|
|
38
|
-
"kysely": "^0.29.
|
|
35
|
+
"@kubun/db-adapter": "^0.18.0",
|
|
36
|
+
"@kubun/db-better-sqlite": "^0.18.0",
|
|
37
|
+
"@kubun/db-postgres": "^0.18.0",
|
|
38
|
+
"@testcontainers/postgresql": "^12.2.1",
|
|
39
|
+
"kysely": "^0.29.7"
|
|
39
40
|
},
|
|
40
41
|
"publishConfig": {
|
|
41
42
|
"access": "public"
|
package/lib/op.d.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { type SigningIdentity } from '@kokuin/token';
|
|
2
|
-
import type { CredentialEntry, CredentialKey, CredentialOpInput, CredentialOpSubjectKind, CredentialTombstoneInput, CredentialWrapping, PutCredentialEntryParams, PutCredentialKeyParams, PutCredentialWrappingParams } from '@kubun/store-credential';
|
|
3
|
-
/**
|
|
4
|
-
* What the author of a credential row signs.
|
|
5
|
-
*
|
|
6
|
-
* Digests rather than bytes, as a grant token does: an op travels beside the row
|
|
7
|
-
* it speaks for, so repeating the ciphertext in the signature would double every
|
|
8
|
-
* vault on the wire.
|
|
9
|
-
*
|
|
10
|
-
* `contentDigest` is `null` exactly when `deleted` — a tombstone has no content,
|
|
11
|
-
* and a digest of nothing would be a constant every tombstone shared.
|
|
12
|
-
*/
|
|
13
|
-
export type CredentialOpClaim = {
|
|
14
|
-
subjectKind: CredentialOpSubjectKind;
|
|
15
|
-
subjectID: string;
|
|
16
|
-
keyID: string;
|
|
17
|
-
keyVersion: number;
|
|
18
|
-
hlc: string;
|
|
19
|
-
contentDigest: string | null;
|
|
20
|
-
deleted: boolean;
|
|
21
|
-
/**
|
|
22
|
-
* The content-key branch a wrapping or entry joins: its introducing key-op's
|
|
23
|
-
* `${hlc}:${opHash}` label, signed so a relay cannot move a row between
|
|
24
|
-
* branches. Absent on a key-op — a key-op's own label folds its own `opHash`,
|
|
25
|
-
* which is unknown until after signing, so it is recomputed on receipt rather
|
|
26
|
-
* than claimed. Absent too on an op minted outside a rotation/create, which
|
|
27
|
-
* does not yet resolve its branch.
|
|
28
|
-
*/
|
|
29
|
-
branchID?: string;
|
|
30
|
-
};
|
|
31
|
-
/** `issuer` is the normalized verified `iss` — the only author claim a receiver trusts. */
|
|
32
|
-
export type VerifiedCredentialOp = {
|
|
33
|
-
issuer: string;
|
|
34
|
-
claim: CredentialOpClaim;
|
|
35
|
-
};
|
|
36
|
-
/** Which row an op speaks for, for the ops that carry no row of their own. */
|
|
37
|
-
export type CredentialOpSubject = {
|
|
38
|
-
subjectKind: CredentialOpSubjectKind;
|
|
39
|
-
subjectID: string;
|
|
40
|
-
keyID: string;
|
|
41
|
-
keyVersion: number;
|
|
42
|
-
};
|
|
43
|
-
/** A stored row, tagged with which of the three tables it came from. */
|
|
44
|
-
export type CredentialRowSubject = {
|
|
45
|
-
kind: 'key';
|
|
46
|
-
row: CredentialKey;
|
|
47
|
-
} | {
|
|
48
|
-
kind: 'wrapping';
|
|
49
|
-
row: CredentialWrapping;
|
|
50
|
-
} | {
|
|
51
|
-
kind: 'entry';
|
|
52
|
-
row: CredentialEntry;
|
|
53
|
-
};
|
|
54
|
-
/**
|
|
55
|
-
* A row about to be written, in the shape the store takes it.
|
|
56
|
-
*
|
|
57
|
-
* The counterpart to {@link CredentialRowSubject} for a caller that holds no row
|
|
58
|
-
* yet — a receiver checking an op against bytes it has not stored. It is also
|
|
59
|
-
* the shape the signer signs, so the two sides compare the same fields.
|
|
60
|
-
*/
|
|
61
|
-
export type CredentialPutSubject = {
|
|
62
|
-
kind: 'key';
|
|
63
|
-
params: PutCredentialKeyParams;
|
|
64
|
-
} | {
|
|
65
|
-
kind: 'wrapping';
|
|
66
|
-
params: PutCredentialWrappingParams;
|
|
67
|
-
} | {
|
|
68
|
-
kind: 'entry';
|
|
69
|
-
params: PutCredentialEntryParams;
|
|
70
|
-
};
|
|
71
|
-
/**
|
|
72
|
-
* Mints the signed op for every credential row this device writes.
|
|
73
|
-
*
|
|
74
|
-
* Separate from the store because the store holds no identity and does no
|
|
75
|
-
* crypto: it takes the token as opaque text, exactly as it takes a ciphertext.
|
|
76
|
-
*/
|
|
77
|
-
export type CredentialOpSigner = {
|
|
78
|
-
key(params: PutCredentialKeyParams, hlc: string): Promise<CredentialOpInput>;
|
|
79
|
-
/**
|
|
80
|
-
* `branchID` is the introducing key-op's label — passed by a create/rotate
|
|
81
|
-
* that just minted that key-op. Omitted elsewhere: an op made outside a
|
|
82
|
-
* rotation does not yet resolve its branch, and gets an empty label until a
|
|
83
|
-
* later slice threads the current one out of the read path.
|
|
84
|
-
*/
|
|
85
|
-
wrapping(params: PutCredentialWrappingParams, hlc: string, branchID?: string): Promise<CredentialOpInput>;
|
|
86
|
-
/**
|
|
87
|
-
* Stamped from the entry's own `hlc`, never a second reading of the clock. The
|
|
88
|
-
* entry row keeps that column and the op repeats it, so the two would
|
|
89
|
-
* otherwise disagree the moment the clock moved between them. `branchID` as on
|
|
90
|
-
* {@link CredentialOpSigner.wrapping}.
|
|
91
|
-
*/
|
|
92
|
-
entry(params: PutCredentialEntryParams, branchID?: string): Promise<CredentialOpInput>;
|
|
93
|
-
tombstone(subject: CredentialOpSubject, hlc: string): Promise<CredentialOpInput>;
|
|
94
|
-
/** A tombstone naming its own subject, for a delete that removes several rows. */
|
|
95
|
-
wrappingTombstone(subject: CredentialOpSubject, hlc: string): Promise<CredentialTombstoneInput>;
|
|
96
|
-
};
|
|
97
|
-
/** The digest of the bytes a row holds, per subject kind. */
|
|
98
|
-
export declare function credentialRowDigest(subject: CredentialRowSubject): string;
|
|
99
|
-
/** The digest of the bytes a row is about to be written with, per subject kind. */
|
|
100
|
-
export declare function credentialPutDigest(subject: CredentialPutSubject): string;
|
|
101
|
-
/** Whether a verified claim speaks for a stored row. */
|
|
102
|
-
export declare function credentialOpMatchesRow(claim: CredentialOpClaim, subject: CredentialRowSubject): boolean;
|
|
103
|
-
/** Whether a verified claim speaks for a row about to be written. */
|
|
104
|
-
export declare function credentialOpMatchesPut(claim: CredentialOpClaim, subject: CredentialPutSubject): boolean;
|
|
105
|
-
export declare function createCredentialOpSigner(identity: SigningIdentity): CredentialOpSigner;
|
|
106
|
-
/**
|
|
107
|
-
* Order two branch labels, keyless and total.
|
|
108
|
-
*
|
|
109
|
-
* The `hlc` halves are compared as strings: the serialized HLC form is
|
|
110
|
-
* lexically total-ordered by construction (fixed-width ISO wall time, then a
|
|
111
|
-
* zero-padded counter, then the nodeID), the same property the store's
|
|
112
|
-
* tombstone check leans on. Two concurrent rotations neither dominate, so the
|
|
113
|
-
* `opHash` breaks the tie — also lexical, and distinct per op, which is what
|
|
114
|
-
* makes the order total and identical on every device.
|
|
115
|
-
*/
|
|
116
|
-
export declare function compareBranchIDs(a: string, b: string): number;
|
|
117
|
-
/**
|
|
118
|
-
* The single branch a keyless party names as the winner: the one whose
|
|
119
|
-
* introducing key-op dominates by HLC, `opHash` breaking genuine concurrency.
|
|
120
|
-
* Order-independent — every device folds the same set to the same label.
|
|
121
|
-
*/
|
|
122
|
-
export declare function selectWinningBranch(branchIDs: ReadonlyArray<string>): string;
|
|
123
|
-
/**
|
|
124
|
-
* Verify an op and extract its claim. Returns `null` (never throws) when the
|
|
125
|
-
* token is unparseable, unsigned (`alg: 'none'`), or structurally malformed — an
|
|
126
|
-
* attacker cannot forge an `iss` this way.
|
|
127
|
-
*/
|
|
128
|
-
export declare function verifyCredentialOp(token: string): Promise<VerifiedCredentialOp | null>;
|