@kubun/store-credential 0.17.3 → 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/advisory-locks.d.ts +5 -0
- package/lib/advisory-locks.js +12 -0
- package/lib/api-aliases.d.ts +3 -0
- package/lib/api-aliases.js +58 -0
- package/lib/api-entries.d.ts +3 -0
- package/lib/api-entries.js +157 -0
- package/lib/api-keys.d.ts +3 -0
- package/lib/api-keys.js +249 -0
- package/lib/api-provisioning.d.ts +3 -0
- package/lib/api-provisioning.js +227 -0
- package/lib/api-records.d.ts +3 -0
- package/lib/api-records.js +74 -0
- package/lib/api-types.d.ts +412 -0
- package/lib/api-types.js +14 -0
- package/lib/api.d.ts +5 -429
- package/lib/api.js +24 -968
- package/lib/definition.js +3 -1
- package/lib/index.d.ts +3 -2
- package/lib/index.js +1 -0
- package/lib/migrations.js +62 -36
- package/lib/records.d.ts +206 -0
- package/lib/records.js +905 -0
- package/lib/store-context.d.ts +37 -0
- package/lib/store-context.js +139 -0
- package/lib/tables.d.ts +88 -64
- package/package.json +11 -8
package/lib/api.d.ts
CHANGED
|
@@ -1,436 +1,12 @@
|
|
|
1
1
|
import type { Adapter } from '@kubun/db-adapter';
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* plugin's signed `CredentialManifestClaim`, so a verified claim can pass
|
|
7
|
-
* straight to {@link CredentialStoreAPI.putManifest} without mapping. This
|
|
8
|
-
* package neither signs nor verifies; the raw token rides alongside as `token`.
|
|
9
|
-
*/
|
|
10
|
-
export type CredentialManifestRecord = {
|
|
11
|
-
ownerDID: string;
|
|
12
|
-
recipientDID: string;
|
|
13
|
-
grantorDID: string;
|
|
14
|
-
epoch: number;
|
|
15
|
-
sequence: number;
|
|
16
|
-
keys: Array<CredentialManifestKey>;
|
|
17
|
-
};
|
|
18
|
-
export type ListManifestsParams = {
|
|
19
|
-
ownerDID: string;
|
|
20
|
-
recipientDID: string;
|
|
21
|
-
};
|
|
22
|
-
/** One stored manifest: its persisted record, the signed token that authored it, and delegation tokens. */
|
|
23
|
-
export type StoredCredentialManifest = {
|
|
24
|
-
record: CredentialManifestRecord;
|
|
25
|
-
token: string;
|
|
26
|
-
delegationTokens: Array<string>;
|
|
27
|
-
};
|
|
28
|
-
export type PutWrappingAuthorityParams = {
|
|
29
|
-
wrappingID: string;
|
|
30
|
-
envelope: string;
|
|
31
|
-
refreshSeq: number;
|
|
32
|
-
};
|
|
33
|
-
export type WrappingAuthority = {
|
|
34
|
-
envelope: string;
|
|
35
|
-
refreshSeq: number;
|
|
36
|
-
};
|
|
37
|
-
export type NextGrantorSequenceParams = {
|
|
38
|
-
ownerDID: string;
|
|
39
|
-
recipientDID: string;
|
|
40
|
-
grantorDID: string;
|
|
41
|
-
};
|
|
42
|
-
export type NextProvisioningEpochParams = {
|
|
43
|
-
ownerDID: string;
|
|
44
|
-
recipientDID: string;
|
|
45
|
-
grantorDID: string;
|
|
46
|
-
minEpoch: number;
|
|
47
|
-
};
|
|
48
|
-
export type ListDistributableKeysParams = {
|
|
49
|
-
ownerDID: string;
|
|
50
|
-
grantorDID: string;
|
|
51
|
-
};
|
|
52
|
-
/**
|
|
53
|
-
* The signed op that produced a row's current state, as the store holds it.
|
|
54
|
-
*
|
|
55
|
-
* `opJWT` is opaque here — this package neither signs nor verifies, the same way
|
|
56
|
-
* it neither encrypts nor decrypts. `@kubun/credential` mints these and is the
|
|
57
|
-
* only thing that can read one.
|
|
58
|
-
*/
|
|
59
|
-
export type CredentialOpInput = {
|
|
60
|
-
keyID: string;
|
|
61
|
-
authorDID: string;
|
|
62
|
-
hlc: string;
|
|
63
|
-
opJWT: string;
|
|
64
|
-
opHash: string;
|
|
65
|
-
/**
|
|
66
|
-
* The content-key branch this op joins — `${hlc}:${opHash}` of the branch's
|
|
67
|
-
* introducing key-op. Empty when the op is minted outside a rotation/create
|
|
68
|
-
* and so does not resolve the branch it belongs to.
|
|
69
|
-
*/
|
|
70
|
-
branchID: string;
|
|
71
|
-
};
|
|
72
|
-
/** A tombstone for one subject of a multi-row delete, which names its own. */
|
|
73
|
-
export type CredentialTombstoneInput = CredentialOpInput & {
|
|
74
|
-
subjectID: string;
|
|
75
|
-
};
|
|
76
|
-
export type PutCredentialKeyParams = {
|
|
77
|
-
keyID: string;
|
|
78
|
-
ownerDID: string;
|
|
79
|
-
suite: number;
|
|
80
|
-
version: number;
|
|
81
|
-
state: CredentialKeyState;
|
|
82
|
-
};
|
|
83
|
-
export type PutCredentialWrappingParams = {
|
|
84
|
-
wrappingID: string;
|
|
85
|
-
keyID: string;
|
|
86
|
-
keyVersion: number;
|
|
87
|
-
factors: Array<CredentialFactorDescriptor>;
|
|
88
|
-
iv: Uint8Array;
|
|
89
|
-
wrappedKey: Uint8Array;
|
|
90
|
-
recipientDID?: string | null;
|
|
91
|
-
};
|
|
92
|
-
export type PutCredentialEntryParams = {
|
|
93
|
-
entryID: string;
|
|
94
|
-
keyID: string;
|
|
95
|
-
keyVersion: number;
|
|
96
|
-
iv: Uint8Array;
|
|
97
|
-
ciphertext: Uint8Array;
|
|
98
|
-
hlc: string;
|
|
99
|
-
};
|
|
100
|
-
export type HeldCredentialSubject = {
|
|
101
|
-
subjectKind: CredentialOpSubjectKind;
|
|
102
|
-
subjectID: string;
|
|
103
|
-
opHash: string;
|
|
104
|
-
};
|
|
105
|
-
/**
|
|
106
|
-
* Persistence for credential keys, wrappings and entries.
|
|
107
|
-
*
|
|
108
|
-
* Storage only: nothing here wraps, unwraps, encrypts or decrypts, and nothing
|
|
109
|
-
* here interprets a ciphertext. `ciphertext` and `wrapped_key` are opaque byte
|
|
110
|
-
* strings that come back exactly as they went in — not parsed as JSON, not
|
|
111
|
-
* decoded as UTF-8, not validated in any way. A caller owning the crypto is what
|
|
112
|
-
* lets the suite change without a migration.
|
|
113
|
-
*
|
|
114
|
-
* A deletion is NOT the absence of a row. It is a tombstone in
|
|
115
|
-
* `kubun_credential_ops`, and the row is gone from its own table — so asking
|
|
116
|
-
* whether a wrapping exists means reading two tables, not one. Absence alone
|
|
117
|
-
* cannot travel over a lane that only adds: a peer holding the deleted row would
|
|
118
|
-
* ship it back and this device would store it again. So a `put` carrying an op no
|
|
119
|
-
* newer than the tombstone writes NOTHING, silently — that is the resend.
|
|
120
|
-
*
|
|
121
|
-
* The op is REQUIRED, and `null` is how a writer says it has none rather than
|
|
122
|
-
* how it says nothing. A row written with `null` is stored but speaks for
|
|
123
|
-
* itself, invisible to the sync lane; making the parameter mandatory is what
|
|
124
|
-
* stops a write path added later from being silently unsigned. Only the GraphQL
|
|
125
|
-
* query context passes it today: it holds a viewer's DID and no signing key.
|
|
126
|
-
*/
|
|
127
|
-
export type CredentialStoreAPI = {
|
|
128
|
-
/**
|
|
129
|
-
* Run `fn` against a `CredentialStoreAPI` scoped to one atomic transaction.
|
|
130
|
-
* Nesting-safe: if `this` is already bound to an open Kysely transaction,
|
|
131
|
-
* `fn` runs inline against `this` instead of opening a second one (Kysely
|
|
132
|
-
* has no nested transactions), so callers can invoke this unconditionally.
|
|
133
|
-
*/
|
|
134
|
-
transaction<R>(fn: (store: CredentialStoreAPI) => Promise<R>): Promise<R>;
|
|
135
|
-
putKey(params: PutCredentialKeyParams, op: CredentialOpInput | null): Promise<void>;
|
|
136
|
-
getKey(keyID: string): Promise<CredentialKey | undefined>;
|
|
137
|
-
listKeys(ownerDID: string): Promise<Array<CredentialKey>>;
|
|
138
|
-
setKeyState(keyID: string, state: CredentialKeyState, op: CredentialOpInput | null): Promise<void>;
|
|
139
|
-
/** Bump a key to a new version. Wrappings and entries at earlier versions stay. */
|
|
140
|
-
setKeyVersion(keyID: string, version: number, op: CredentialOpInput | null): Promise<void>;
|
|
141
|
-
deleteKey(keyID: string, tombstone: CredentialOpInput | null): Promise<void>;
|
|
142
|
-
recordKeyBranch(params: {
|
|
143
|
-
keyID: string;
|
|
144
|
-
keyVersion: number;
|
|
145
|
-
authorDID: string;
|
|
146
|
-
hlc: string;
|
|
147
|
-
opJWT: string;
|
|
148
|
-
opHash: string;
|
|
149
|
-
branchID: string;
|
|
150
|
-
}): Promise<void>;
|
|
151
|
-
listKeyBranches(keyID: string, keyVersion: number): Promise<Array<CredentialKeyBranch>>;
|
|
152
|
-
/**
|
|
153
|
-
* `authority`, when present, upserts the wrapping's grantor-authority envelope
|
|
154
|
-
* in the SAME transaction as the wrapping row and its op, so a crash can't
|
|
155
|
-
* leave a held wrapping this device can't later prove was authorized. Omitted
|
|
156
|
-
* for an owner-signed wrapping. Gated strictly-greater on `refreshSeq`, same
|
|
157
|
-
* as {@link CredentialStoreAPI.putWrappingAuthority}.
|
|
158
|
-
*/
|
|
159
|
-
putWrapping(params: PutCredentialWrappingParams, op: CredentialOpInput | null, authority?: {
|
|
160
|
-
envelope: string;
|
|
161
|
-
refreshSeq: number;
|
|
162
|
-
}): Promise<void>;
|
|
163
|
-
getWrapping(wrappingID: string): Promise<CredentialWrapping | undefined>;
|
|
164
|
-
/** Every wrapping for a key, or only those at one version. */
|
|
165
|
-
listWrappings(keyID: string, keyVersion?: number): Promise<Array<CredentialWrapping>>;
|
|
166
|
-
/**
|
|
167
|
-
* As {@link listWrappings}, each row carrying its op's `branch_id`. The
|
|
168
|
-
* fork-aware read path needs the label to resolve which content-key branch a
|
|
169
|
-
* device opens; every other caller reads none, so the join stays off the
|
|
170
|
-
* common path.
|
|
171
|
-
*/
|
|
172
|
-
listWrappingsWithBranch(keyID: string, keyVersion?: number): Promise<Array<CredentialWrappingWithBranch>>;
|
|
173
|
-
deleteWrapping(wrappingID: string, tombstone: CredentialOpInput | null): Promise<void>;
|
|
174
|
-
/**
|
|
175
|
-
* Drop every wrapping for a key below `keyVersion` — what a rotation leaves
|
|
176
|
-
* behind. The tombstones name their own subjects, because the caller decides
|
|
177
|
-
* which rows it is accounting for and the delete predicate spans many.
|
|
178
|
-
*/
|
|
179
|
-
deleteWrappingsBelowVersion(keyID: string, keyVersion: number, tombstones: Array<CredentialTombstoneInput> | null): Promise<void>;
|
|
180
|
-
putEntry(params: PutCredentialEntryParams, op: CredentialOpInput | null): Promise<void>;
|
|
181
|
-
/**
|
|
182
|
-
* Explicitly change an entry's key association. Callers must authorize both
|
|
183
|
-
* source and target keys before invoking this capability.
|
|
184
|
-
*/
|
|
185
|
-
moveEntry(params: PutCredentialEntryParams, op: CredentialOpInput | null): Promise<void>;
|
|
186
|
-
/**
|
|
187
|
-
* Write over an entry's ciphertext ONLY if the row already exists under the
|
|
188
|
-
* same key — never inserts, never moves it. Op-first like putEntry: the op is
|
|
189
|
-
* written under the op-conflict lock, then the row; if no matching row is
|
|
190
|
-
* present (concurrently deleted, or re-keyed away) the op is rolled back, so a
|
|
191
|
-
* refresh writeback can neither revive a concurrently-deleted entry nor leave
|
|
192
|
-
* an op stranded without its row. Returns 'absent' when nothing was updated.
|
|
193
|
-
*/
|
|
194
|
-
updateEntryIfPresent(params: PutCredentialEntryParams, op: CredentialOpInput | null): Promise<'updated' | 'absent'>;
|
|
195
|
-
getEntry(entryID: string): Promise<CredentialEntry | undefined>;
|
|
196
|
-
getEntryWithProvenance(entryID: string): Promise<CredentialEntryWithProvenance | undefined>;
|
|
197
|
-
listEntries(keyID: string): Promise<Array<CredentialEntry>>;
|
|
198
|
-
/**
|
|
199
|
-
* Greatest stored entry HLC, or `null` when there are none. Credential writes
|
|
200
|
-
* advance the device clock but leave no row in the graph mutation log, so this
|
|
201
|
-
* is the only trace they leave for a restart floor to read.
|
|
202
|
-
*/
|
|
203
|
-
getMaxEntryHLC(): Promise<string | null>;
|
|
204
|
-
deleteEntry(entryID: string, tombstone: CredentialOpInput | null): Promise<void>;
|
|
205
|
-
/** The op a subject's current state came from, tombstone included. */
|
|
206
|
-
getOp(subjectKind: CredentialOpSubjectKind, subjectID: string): Promise<CredentialOp | undefined>;
|
|
207
|
-
/**
|
|
208
|
-
* Ops in stamp order — every one, or one key's. This is the merkle lane's leaf
|
|
209
|
-
* scan, which is why the ops of all three subject kinds share a table.
|
|
210
|
-
*/
|
|
211
|
-
listOps(keyID?: string): Promise<Array<CredentialOp>>;
|
|
212
|
-
/**
|
|
213
|
-
* The leaf scan scoped to what a requesting peer may hold.
|
|
214
|
-
*
|
|
215
|
-
* The document lane serves a tree scoped by read-ACL, but a credential is not
|
|
216
|
-
* readable by scope: a group co-member holds no wrapping under someone else's
|
|
217
|
-
* key, so serving them its rows ships ciphertext they can never open and reveals
|
|
218
|
-
* the key exists. The predicate is recipient-addressed instead. A key's row and
|
|
219
|
-
* its entry ops travel to any recipient of the key — a peer holding a wrapping
|
|
220
|
-
* under it. A wrapping's op travels only to the DID it names, so a recipient
|
|
221
|
-
* never receives a co-recipient's wrapping. Recipient match folds both sides
|
|
222
|
-
* with `normalizeDID`, because `recipient_did` is stored un-normalized.
|
|
223
|
-
*/
|
|
224
|
-
listOpsForRequester(requesterDID: string, keyID?: string): Promise<Array<CredentialOp>>;
|
|
225
|
-
/**
|
|
226
|
-
* The live (non-tombstoned) subject ids this device holds — keys, wrappings and
|
|
227
|
-
* entries alike. What a reconcile caller advertises as `held`: the server reads
|
|
228
|
-
* it to skip rows the caller already has and to route a tombstone for a held id
|
|
229
|
-
* that is now revoked.
|
|
230
|
-
*/
|
|
231
|
-
listHeldSubjects(): Promise<Array<HeldCredentialSubject>>;
|
|
232
|
-
/**
|
|
233
|
-
* The entry id currently pointed to by an opaque `(namespace, key)`, or
|
|
234
|
-
* `null` when unset. Neither argument is interpreted — a caller normalizes
|
|
235
|
-
* whatever it means by "key" before calling.
|
|
236
|
-
*/
|
|
237
|
-
getAliasEntryID(namespace: string, key: string): Promise<string | null>;
|
|
238
|
-
/** Point `(namespace, key)` at `entryID`, replacing any prior target. */
|
|
239
|
-
setAliasEntryID(namespace: string, key: string, entryID: string): Promise<void>;
|
|
240
|
-
/**
|
|
241
|
-
* Bind `(namespace, aliasKey)` to `entryID` only if the alias slot is free and
|
|
242
|
-
* the entry exists — unlike {@link setAliasEntryID}, this never overwrites an
|
|
243
|
-
* existing alias and never binds a nonexistent entry. Classifies the outcome
|
|
244
|
-
* instead of throwing, since both unique constraints are expected outcomes a
|
|
245
|
-
* caller branches on, not exceptional ones.
|
|
246
|
-
*/
|
|
247
|
-
bindAliasIfAbsent(namespace: string, aliasKey: string, entryID: string): Promise<'inserted' | 'entry-missing' | {
|
|
248
|
-
conflict: 'alias';
|
|
249
|
-
existing: string;
|
|
250
|
-
} | {
|
|
251
|
-
conflict: 'entry';
|
|
252
|
-
}>;
|
|
253
|
-
/** Clear an alias. A no-op, not an error, when none is set. */
|
|
254
|
-
deleteAliasEntryID(namespace: string, key: string): Promise<void>;
|
|
255
|
-
/**
|
|
256
|
-
* Clear an alias only while it still points at `entryID`. A concurrent rebind
|
|
257
|
-
* that repaired the pointer onto a fresh entry in the gap after the old entry
|
|
258
|
-
* was deleted is left intact; a no-op when the pointer already moved or is unset.
|
|
259
|
-
*/
|
|
260
|
-
deleteAliasIfEntry(namespace: string, key: string, entryID: string): Promise<void>;
|
|
261
|
-
/**
|
|
262
|
-
* The distinct keys currently set within `namespace`. Keys stay opaque here —
|
|
263
|
-
* a caller interprets them (e.g. connector credentials store the normalized
|
|
264
|
-
* owner DID as the key under a provider namespace). Empty for an unused
|
|
265
|
-
* namespace.
|
|
266
|
-
*/
|
|
267
|
-
listAliasKeys(namespace: string): Promise<Array<string>>;
|
|
268
|
-
/** Store a wrapping's grantor-authority envelope; gated strictly-greater on `refreshSeq` (stale/replayed is a silent no-op). */
|
|
269
|
-
putWrappingAuthority(params: PutWrappingAuthorityParams): Promise<void>;
|
|
270
|
-
/** The stored envelope and its `refreshSeq`, or `undefined` when unset. */
|
|
271
|
-
getWrappingAuthority(wrappingID: string): Promise<WrappingAuthority | undefined>;
|
|
272
|
-
/**
|
|
273
|
-
* Mint the next `refreshSeq` for one `(ownerDID, recipientDID, grantorDID)`
|
|
274
|
-
* triple: 1 on the first call, strictly increasing thereafter. Persisted, so
|
|
275
|
-
* a restart never reissues a number already handed out.
|
|
276
|
-
*/
|
|
277
|
-
nextGrantorSequence(params: NextGrantorSequenceParams): Promise<number>;
|
|
278
|
-
/**
|
|
279
|
-
* Allocate the next `epoch` for one `(ownerDID, recipientDID, grantorDID)`
|
|
280
|
-
* triple: returns `max(stored, minEpoch) + 1` on each call, so the value is
|
|
281
|
-
* strictly increasing but a floor can jump it forward. Persisted; never
|
|
282
|
-
* reissues an epoch already handed out. Triple is keyed on normalized DIDs,
|
|
283
|
-
* so a `did:peer:4` short/long form converge on one row.
|
|
284
|
-
*/
|
|
285
|
-
nextProvisioningEpoch(params: NextProvisioningEpochParams): Promise<number>;
|
|
286
|
-
/**
|
|
287
|
-
* Store a grantor's manifest, gated lexicographically on `(epoch, sequence)`
|
|
288
|
-
* per `(ownerDID, recipientDID, grantorDID)` triple — higher epoch always
|
|
289
|
-
* wins, sequence breaks ties within an epoch; a stale/replayed value is a
|
|
290
|
-
* silent no-op. Triple keyed on normalized DIDs. `token` is the raw signed
|
|
291
|
-
* manifest; `delegationTokens` are optional wrappings that authorized it.
|
|
292
|
-
*/
|
|
293
|
-
putManifest(record: CredentialManifestRecord, token: string, delegationTokens?: Array<string>): Promise<void>;
|
|
294
|
-
/**
|
|
295
|
-
* Every grantor's manifest for one `(ownerDID, recipientDID)`, one row per
|
|
296
|
-
* grantor. Arguments are normalized before query, matching
|
|
297
|
-
* {@link CredentialStoreAPI.putManifest}'s normalize-on-write.
|
|
298
|
-
*/
|
|
299
|
-
listManifests(params: ListManifestsParams): Promise<Array<StoredCredentialManifest>>;
|
|
300
|
-
/**
|
|
301
|
-
* Take the write lock on the manifest row so a held-epoch read later in the
|
|
302
|
-
* SAME transaction can't race a concurrent {@link CredentialStoreAPI.putManifest}.
|
|
303
|
-
* A value-preserving UPDATE, not `FOR UPDATE` (SQLite rejects that syntax) —
|
|
304
|
-
* it blocks a concurrent Postgres writer the same way; SQLite is already
|
|
305
|
-
* single-writer-serialized. No-op if the row doesn't exist yet; that
|
|
306
|
-
* first-write race is benign (held epoch 0).
|
|
307
|
-
*/
|
|
308
|
-
lockManifestRow(params: {
|
|
309
|
-
ownerDID: string;
|
|
310
|
-
recipientDID: string;
|
|
311
|
-
grantorDID: string;
|
|
312
|
-
}): Promise<void>;
|
|
313
|
-
/**
|
|
314
|
-
* Every active owner key for which a wrapping exists at the key's current
|
|
315
|
-
* version addressed (after normalization) to the grantor. One snapshot read;
|
|
316
|
-
* in-memory DID normalization. Deduped by keyID.
|
|
317
|
-
*/
|
|
318
|
-
listDistributableKeys(params: ListDistributableKeysParams): Promise<Array<{
|
|
319
|
-
keyID: string;
|
|
320
|
-
version: number;
|
|
321
|
-
}>>;
|
|
322
|
-
/**
|
|
323
|
-
* Begin tracking a provisioning attempt, idempotent on the id. Ensures an
|
|
324
|
-
* expectation floor row exists (at 0) if none yet. The attempt counts toward
|
|
325
|
-
* outstanding and blocks readiness until cleared.
|
|
326
|
-
*/
|
|
327
|
-
beginProvisioningAttempt(params: {
|
|
328
|
-
ownerDID: string;
|
|
329
|
-
grantorDID: string;
|
|
330
|
-
attemptID: string;
|
|
331
|
-
}): Promise<void>;
|
|
332
|
-
/**
|
|
333
|
-
* Record a provisioning attempt's outcome, atomically. If `epoch > heldEpoch`,
|
|
334
|
-
* clears the attempt row and raises the expectation floor to
|
|
335
|
-
* `max(stored, epoch)`; otherwise leaves the attempt row and returns retry.
|
|
336
|
-
*/
|
|
337
|
-
recordProvisioningAttempt(params: {
|
|
338
|
-
ownerDID: string;
|
|
339
|
-
grantorDID: string;
|
|
340
|
-
attemptID: string;
|
|
341
|
-
epoch: number;
|
|
342
|
-
heldEpoch: number;
|
|
343
|
-
}): Promise<{
|
|
344
|
-
status: 'recorded';
|
|
345
|
-
} | {
|
|
346
|
-
status: 'retry';
|
|
347
|
-
minEpoch: number;
|
|
348
|
-
}>;
|
|
349
|
-
/**
|
|
350
|
-
* Abandon a provisioning attempt. Deletes ONLY the attempt row, never touches
|
|
351
|
-
* the expectation floor.
|
|
352
|
-
*/
|
|
353
|
-
abandonProvisioningAttempt(params: {
|
|
354
|
-
ownerDID: string;
|
|
355
|
-
grantorDID: string;
|
|
356
|
-
attemptID: string;
|
|
357
|
-
}): Promise<void>;
|
|
358
|
-
/**
|
|
359
|
-
* Abandon ALL outstanding provisioning attempts for one (owner, grantor)
|
|
360
|
-
* scope. Deletes every attempt row for the pair, never touches the
|
|
361
|
-
* expectation floor. The recovery escape hatch when the attempt id is lost.
|
|
362
|
-
*
|
|
363
|
-
* Exclusive-recovery contract: this also clears any OTHER live coordinator's
|
|
364
|
-
* attempt on the same scope (whose later `recordProvisioningAttempt` would
|
|
365
|
-
* then hit the phantom-attempt guard and not raise the floor). Callers must
|
|
366
|
-
* ensure no other live attempt exists for the scope before calling. When the
|
|
367
|
-
* id is known, prefer `abandonProvisioningAttempt`, which is safe under
|
|
368
|
-
* concurrency.
|
|
369
|
-
*/
|
|
370
|
-
abandonProvisioningAttempts(params: {
|
|
371
|
-
ownerDID: string;
|
|
372
|
-
grantorDID: string;
|
|
373
|
-
}): Promise<void>;
|
|
374
|
-
/**
|
|
375
|
-
* List provisioning expectations for one owner: one row per (owner, grantor)
|
|
376
|
-
* pair currently held. Returns the expectation floor and count of outstanding
|
|
377
|
-
* attempts. Normalizes ownerDID on read.
|
|
378
|
-
*/
|
|
379
|
-
listProvisioningExpectations(params: {
|
|
380
|
-
ownerDID: string;
|
|
381
|
-
}): Promise<Array<{
|
|
382
|
-
grantorDID: string;
|
|
383
|
-
epochFloor: number;
|
|
384
|
-
outstanding: number;
|
|
385
|
-
}>>;
|
|
386
|
-
/**
|
|
387
|
-
* Every distinct owner DID with a provisioning expectation row, normalized
|
|
388
|
-
* and de-duped — the catalog-independent enumeration
|
|
389
|
-
* `listProvisioningExpectations` can't give on its own (it's per-owner).
|
|
390
|
-
*/
|
|
391
|
-
listProvisioningExpectationOwners(): Promise<Array<string>>;
|
|
392
|
-
/**
|
|
393
|
-
* Refresh a controller-owned key's `credential/administer` delegation chain,
|
|
394
|
-
* gated strictly-greater on `refreshSeq` (stale/replayed is a silent no-op,
|
|
395
|
-
* same shape as {@link CredentialStoreAPI.putWrappingAuthority}). Handles
|
|
396
|
-
* both the absent-row insert and the monotone race. Returns whether a row
|
|
397
|
-
* was inserted or updated.
|
|
398
|
-
*/
|
|
399
|
-
refreshKeyOwnerDelegation(params: {
|
|
400
|
-
keyID: string;
|
|
401
|
-
delegationTokens: Array<string>;
|
|
402
|
-
refreshSeq: number;
|
|
403
|
-
}): Promise<boolean>;
|
|
404
|
-
/**
|
|
405
|
-
* Compare-and-swap a key-owner delegation chain, guarded on the `revision`
|
|
406
|
-
* the caller read. Replaces the row only when its CURRENT `revision` still
|
|
407
|
-
* equals `expectedRevision`, so a writer that read an invalid-present chain
|
|
408
|
-
* and revalidated it can install a replacement without racing another such
|
|
409
|
-
* writer or a concurrent refresh. The guard is `revision`, NOT `refreshSeq`:
|
|
410
|
-
* two distinct valid chains can share a `refreshSeq` (min-exp ms), so guarding
|
|
411
|
-
* on the seq has an ABA hole. Unlike {@link refreshKeyOwnerDelegation} this
|
|
412
|
-
* never inserts and is not monotone — it is the escape hatch for the
|
|
413
|
-
* invalid-present case, where the incoming chain's `refreshSeq` may be lower.
|
|
414
|
-
* Absent row is a no-op false, same as a revision mismatch. A successful
|
|
415
|
-
* write bumps `revision`.
|
|
416
|
-
*/
|
|
417
|
-
replaceKeyOwnerDelegationIfUnchanged(params: {
|
|
418
|
-
keyID: string;
|
|
419
|
-
expectedRevision: number;
|
|
420
|
-
delegationTokens: Array<string>;
|
|
421
|
-
refreshSeq: number;
|
|
422
|
-
}): Promise<boolean>;
|
|
423
|
-
/** The stored delegation chain, its `refreshSeq`, and its `revision`, or `undefined` when unset. */
|
|
424
|
-
getKeyOwnerDelegation(keyID: string): Promise<{
|
|
425
|
-
delegationTokens: Array<string>;
|
|
426
|
-
refreshSeq: number;
|
|
427
|
-
revision: number;
|
|
428
|
-
} | undefined>;
|
|
429
|
-
};
|
|
2
|
+
import type { Kysely } from 'kysely';
|
|
3
|
+
import type { CredentialStoreAPI } from './api-types.js';
|
|
4
|
+
import type { CredentialTables } from './tables.js';
|
|
5
|
+
export type * from './api-types.js';
|
|
430
6
|
/**
|
|
431
7
|
* `refreshSeq` sentinel for a delegation chain that has no future refresh —
|
|
432
8
|
* larger than any real ms timestamp, so a bounded refresh can never overtake
|
|
433
9
|
* it. Consumed by `@kubun/plugin-p2p`.
|
|
434
10
|
*/
|
|
435
11
|
export declare const UNBOUNDED_REFRESH_SEQ = 8640000000000000;
|
|
436
|
-
export declare function createCredentialStore(db: Kysely<CredentialTables>, adapter: Adapter): CredentialStoreAPI;
|
|
12
|
+
export declare function createCredentialStore(db: Kysely<CredentialTables>, adapter: Adapter, advisoryIDForKey?: (keyID: string) => bigint | Promise<bigint>): CredentialStoreAPI;
|