was-teaching-server 0.9.1
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/CHANGELOG.md +1432 -0
- package/LICENSE +661 -0
- package/README.md +305 -0
- package/common/css/materialize.1.0.0.min.css +13 -0
- package/dist/auth-header-hooks.d.ts +84 -0
- package/dist/auth-header-hooks.d.ts.map +1 -0
- package/dist/auth-header-hooks.js +136 -0
- package/dist/auth-header-hooks.js.map +1 -0
- package/dist/authorize.d.ts +45 -0
- package/dist/authorize.d.ts.map +1 -0
- package/dist/authorize.js +76 -0
- package/dist/authorize.js.map +1 -0
- package/dist/backends/filesystem.d.ts +1179 -0
- package/dist/backends/filesystem.d.ts.map +1 -0
- package/dist/backends/filesystem.js +2727 -0
- package/dist/backends/filesystem.js.map +1 -0
- package/dist/backends/postgres.d.ts +833 -0
- package/dist/backends/postgres.d.ts.map +1 -0
- package/dist/backends/postgres.js +2164 -0
- package/dist/backends/postgres.js.map +1 -0
- package/dist/backends/postgresSchema.d.ts +36 -0
- package/dist/backends/postgresSchema.d.ts.map +1 -0
- package/dist/backends/postgresSchema.js +203 -0
- package/dist/backends/postgresSchema.js.map +1 -0
- package/dist/config.default.d.ts +360 -0
- package/dist/config.default.d.ts.map +1 -0
- package/dist/config.default.js +493 -0
- package/dist/config.default.js.map +1 -0
- package/dist/corsProxy.d.ts +45 -0
- package/dist/corsProxy.d.ts.map +1 -0
- package/dist/corsProxy.js +278 -0
- package/dist/corsProxy.js.map +1 -0
- package/dist/digest.d.ts +45 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +174 -0
- package/dist/digest.js.map +1 -0
- package/dist/errors.d.ts +654 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +960 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/atomicFile.d.ts +61 -0
- package/dist/lib/atomicFile.d.ts.map +1 -0
- package/dist/lib/atomicFile.js +150 -0
- package/dist/lib/atomicFile.js.map +1 -0
- package/dist/lib/backendRegistry.d.ts +59 -0
- package/dist/lib/backendRegistry.d.ts.map +1 -0
- package/dist/lib/backendRegistry.js +92 -0
- package/dist/lib/backendRegistry.js.map +1 -0
- package/dist/lib/backendUsage.d.ts +28 -0
- package/dist/lib/backendUsage.d.ts.map +1 -0
- package/dist/lib/backendUsage.js +55 -0
- package/dist/lib/backendUsage.js.map +1 -0
- package/dist/lib/backends.d.ts +142 -0
- package/dist/lib/backends.d.ts.map +1 -0
- package/dist/lib/backends.js +272 -0
- package/dist/lib/backends.js.map +1 -0
- package/dist/lib/blindedIndex.d.ts +156 -0
- package/dist/lib/blindedIndex.d.ts.map +1 -0
- package/dist/lib/blindedIndex.js +338 -0
- package/dist/lib/blindedIndex.js.map +1 -0
- package/dist/lib/collectionDescription.d.ts +33 -0
- package/dist/lib/collectionDescription.d.ts.map +1 -0
- package/dist/lib/collectionDescription.js +21 -0
- package/dist/lib/collectionDescription.js.map +1 -0
- package/dist/lib/cursor.d.ts +18 -0
- package/dist/lib/cursor.d.ts.map +1 -0
- package/dist/lib/cursor.js +50 -0
- package/dist/lib/cursor.js.map +1 -0
- package/dist/lib/edvEnvelope.d.ts +50 -0
- package/dist/lib/edvEnvelope.d.ts.map +1 -0
- package/dist/lib/edvEnvelope.js +106 -0
- package/dist/lib/edvEnvelope.js.map +1 -0
- package/dist/lib/encryption.d.ts +192 -0
- package/dist/lib/encryption.d.ts.map +1 -0
- package/dist/lib/encryption.js +383 -0
- package/dist/lib/encryption.js.map +1 -0
- package/dist/lib/etag.d.ts +33 -0
- package/dist/lib/etag.d.ts.map +1 -0
- package/dist/lib/etag.js +36 -0
- package/dist/lib/etag.js.map +1 -0
- package/dist/lib/exportManifest.d.ts +29 -0
- package/dist/lib/exportManifest.d.ts.map +1 -0
- package/dist/lib/exportManifest.js +77 -0
- package/dist/lib/exportManifest.js.map +1 -0
- package/dist/lib/importTar.d.ts +81 -0
- package/dist/lib/importTar.d.ts.map +1 -0
- package/dist/lib/importTar.js +301 -0
- package/dist/lib/importTar.js.map +1 -0
- package/dist/lib/isJson.d.ts +16 -0
- package/dist/lib/isJson.d.ts.map +1 -0
- package/dist/lib/isJson.js +17 -0
- package/dist/lib/isJson.js.map +1 -0
- package/dist/lib/keyEpoch.d.ts +38 -0
- package/dist/lib/keyEpoch.d.ts.map +1 -0
- package/dist/lib/keyEpoch.js +75 -0
- package/dist/lib/keyEpoch.js.map +1 -0
- package/dist/lib/keyedMutex.d.ts +28 -0
- package/dist/lib/keyedMutex.d.ts.map +1 -0
- package/dist/lib/keyedMutex.js +45 -0
- package/dist/lib/keyedMutex.js.map +1 -0
- package/dist/lib/kmsModule.d.ts +62 -0
- package/dist/lib/kmsModule.d.ts.map +1 -0
- package/dist/lib/kmsModule.js +327 -0
- package/dist/lib/kmsModule.js.map +1 -0
- package/dist/lib/kmsRecordCipher.d.ts +82 -0
- package/dist/lib/kmsRecordCipher.d.ts.map +1 -0
- package/dist/lib/kmsRecordCipher.js +226 -0
- package/dist/lib/kmsRecordCipher.js.map +1 -0
- package/dist/lib/pagination.d.ts +19 -0
- package/dist/lib/pagination.d.ts.map +1 -0
- package/dist/lib/pagination.js +21 -0
- package/dist/lib/pagination.js.map +1 -0
- package/dist/lib/paths.d.ts +273 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +270 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/preconditions.d.ts +65 -0
- package/dist/lib/preconditions.d.ts.map +1 -0
- package/dist/lib/preconditions.js +112 -0
- package/dist/lib/preconditions.js.map +1 -0
- package/dist/lib/resourceFileName.d.ts +41 -0
- package/dist/lib/resourceFileName.d.ts.map +1 -0
- package/dist/lib/resourceFileName.js +58 -0
- package/dist/lib/resourceFileName.js.map +1 -0
- package/dist/lib/revocations.d.ts +49 -0
- package/dist/lib/revocations.d.ts.map +1 -0
- package/dist/lib/revocations.js +86 -0
- package/dist/lib/revocations.js.map +1 -0
- package/dist/lib/validateDid.d.ts +19 -0
- package/dist/lib/validateDid.d.ts.map +1 -0
- package/dist/lib/validateDid.js +35 -0
- package/dist/lib/validateDid.js.map +1 -0
- package/dist/lib/validateId.d.ts +66 -0
- package/dist/lib/validateId.d.ts.map +1 -0
- package/dist/lib/validateId.js +126 -0
- package/dist/lib/validateId.js.map +1 -0
- package/dist/plugin.d.ts +115 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +134 -0
- package/dist/plugin.js.map +1 -0
- package/dist/policy.d.ts +78 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +115 -0
- package/dist/policy.js.map +1 -0
- package/dist/provisioning.d.ts +38 -0
- package/dist/provisioning.d.ts.map +1 -0
- package/dist/provisioning.js +72 -0
- package/dist/provisioning.js.map +1 -0
- package/dist/requests/BackendRequest.d.ts +61 -0
- package/dist/requests/BackendRequest.d.ts.map +1 -0
- package/dist/requests/BackendRequest.js +152 -0
- package/dist/requests/BackendRequest.js.map +1 -0
- package/dist/requests/CollectionRequest.d.ts +234 -0
- package/dist/requests/CollectionRequest.d.ts.map +1 -0
- package/dist/requests/CollectionRequest.js +705 -0
- package/dist/requests/CollectionRequest.js.map +1 -0
- package/dist/requests/KeyRequest.d.ts +115 -0
- package/dist/requests/KeyRequest.d.ts.map +1 -0
- package/dist/requests/KeyRequest.js +468 -0
- package/dist/requests/KeyRequest.js.map +1 -0
- package/dist/requests/KeystoreRequest.d.ts +99 -0
- package/dist/requests/KeystoreRequest.d.ts.map +1 -0
- package/dist/requests/KeystoreRequest.js +247 -0
- package/dist/requests/KeystoreRequest.js.map +1 -0
- package/dist/requests/PolicyRequest.d.ts +53 -0
- package/dist/requests/PolicyRequest.d.ts.map +1 -0
- package/dist/requests/PolicyRequest.js +124 -0
- package/dist/requests/PolicyRequest.js.map +1 -0
- package/dist/requests/ResourceRequest.d.ts +134 -0
- package/dist/requests/ResourceRequest.d.ts.map +1 -0
- package/dist/requests/ResourceRequest.js +548 -0
- package/dist/requests/ResourceRequest.js.map +1 -0
- package/dist/requests/RevocationRequest.d.ts +67 -0
- package/dist/requests/RevocationRequest.d.ts.map +1 -0
- package/dist/requests/RevocationRequest.js +187 -0
- package/dist/requests/RevocationRequest.js.map +1 -0
- package/dist/requests/SpaceRequest.d.ts +204 -0
- package/dist/requests/SpaceRequest.d.ts.map +1 -0
- package/dist/requests/SpaceRequest.js +520 -0
- package/dist/requests/SpaceRequest.js.map +1 -0
- package/dist/requests/SpacesRepositoryRequest.d.ts +50 -0
- package/dist/requests/SpacesRepositoryRequest.d.ts.map +1 -0
- package/dist/requests/SpacesRepositoryRequest.js +159 -0
- package/dist/requests/SpacesRepositoryRequest.js.map +1 -0
- package/dist/requests/collectionContext.d.ts +21 -0
- package/dist/requests/collectionContext.d.ts.map +1 -0
- package/dist/requests/collectionContext.js +30 -0
- package/dist/requests/collectionContext.js.map +1 -0
- package/dist/requests/controllerConsent.d.ts +58 -0
- package/dist/requests/controllerConsent.d.ts.map +1 -0
- package/dist/requests/controllerConsent.js +73 -0
- package/dist/requests/controllerConsent.js.map +1 -0
- package/dist/requests/keystoreContext.d.ts +68 -0
- package/dist/requests/keystoreContext.d.ts.map +1 -0
- package/dist/requests/keystoreContext.js +87 -0
- package/dist/requests/keystoreContext.js.map +1 -0
- package/dist/requests/resourceInput.d.ts +19 -0
- package/dist/requests/resourceInput.d.ts.map +1 -0
- package/dist/requests/resourceInput.js +102 -0
- package/dist/requests/resourceInput.js.map +1 -0
- package/dist/requests/spaceContext.d.ts +127 -0
- package/dist/requests/spaceContext.d.ts.map +1 -0
- package/dist/requests/spaceContext.js +201 -0
- package/dist/requests/spaceContext.js.map +1 -0
- package/dist/routes.d.ts +55 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +306 -0
- package/dist/routes.js.map +1 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +60 -0
- package/dist/server.js.map +1 -0
- package/dist/start.d.ts +7 -0
- package/dist/start.d.ts.map +1 -0
- package/dist/start.js +61 -0
- package/dist/start.js.map +1 -0
- package/dist/storage.d.ts +29 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +47 -0
- package/dist/storage.js.map +1 -0
- package/dist/types.d.ts +915 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/views/home.hbs +9 -0
- package/dist/views/templates/main.hbs +12 -0
- package/dist/zcap.d.ts +267 -0
- package/dist/zcap.d.ts.map +1 -0
- package/dist/zcap.js +478 -0
- package/dist/zcap.js.map +1 -0
- package/package.json +109 -0
- package/src/auth-header-hooks.ts +163 -0
- package/src/authorize.ts +117 -0
- package/src/backends/filesystem.ts +3626 -0
- package/src/backends/postgres.ts +3073 -0
- package/src/backends/postgresSchema.ts +231 -0
- package/src/config.default.ts +657 -0
- package/src/corsProxy.ts +326 -0
- package/src/declarations.d.ts +28 -0
- package/src/digest.ts +223 -0
- package/src/errors.ts +1122 -0
- package/src/index.ts +21 -0
- package/src/lib/atomicFile.ts +176 -0
- package/src/lib/backendRegistry.ts +139 -0
- package/src/lib/backendUsage.ts +79 -0
- package/src/lib/backends.ts +351 -0
- package/src/lib/blindedIndex.ts +466 -0
- package/src/lib/collectionDescription.ts +43 -0
- package/src/lib/cursor.ts +52 -0
- package/src/lib/edvEnvelope.ts +120 -0
- package/src/lib/encryption.ts +472 -0
- package/src/lib/etag.ts +40 -0
- package/src/lib/exportManifest.ts +115 -0
- package/src/lib/importTar.ts +381 -0
- package/src/lib/isJson.ts +18 -0
- package/src/lib/keyEpoch.ts +89 -0
- package/src/lib/keyedMutex.ts +45 -0
- package/src/lib/kmsModule.ts +451 -0
- package/src/lib/kmsRecordCipher.ts +292 -0
- package/src/lib/pagination.ts +22 -0
- package/src/lib/paths.ts +382 -0
- package/src/lib/preconditions.ts +145 -0
- package/src/lib/resourceFileName.ts +69 -0
- package/src/lib/revocations.ts +116 -0
- package/src/lib/validateDid.ts +41 -0
- package/src/lib/validateId.ts +148 -0
- package/src/plugin.ts +288 -0
- package/src/policy.ts +176 -0
- package/src/provisioning.ts +98 -0
- package/src/requests/BackendRequest.ts +219 -0
- package/src/requests/CollectionRequest.ts +899 -0
- package/src/requests/KeyRequest.ts +617 -0
- package/src/requests/KeystoreRequest.ts +325 -0
- package/src/requests/PolicyRequest.ts +169 -0
- package/src/requests/ResourceRequest.ts +649 -0
- package/src/requests/RevocationRequest.ts +267 -0
- package/src/requests/SpaceRequest.ts +686 -0
- package/src/requests/SpacesRepositoryRequest.ts +194 -0
- package/src/requests/collectionContext.ts +41 -0
- package/src/requests/controllerConsent.ts +108 -0
- package/src/requests/keystoreContext.ts +124 -0
- package/src/requests/resourceInput.ts +118 -0
- package/src/requests/spaceContext.ts +311 -0
- package/src/routes.ts +420 -0
- package/src/server.ts +69 -0
- package/src/start.ts +71 -0
- package/src/storage.ts +60 -0
- package/src/types.ts +1016 -0
- package/src/views/home.hbs +9 -0
- package/src/views/templates/main.hbs +12 -0
- package/src/zcap.ts +657 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,1016 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared domain types for the WAS server.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the relevant `@interop/data-integrity-core` types (DIDs, zCaps,
|
|
5
|
+
* document-loader, verifier / key material) and the shared WAS wire model from
|
|
6
|
+
* `@interop/storage-core` so the rest of the codebase imports them from a
|
|
7
|
+
* single place, and defines the server-local domain shapes (the parsed-zcap
|
|
8
|
+
* request shape, the transport-neutral resource input, the get-resource result)
|
|
9
|
+
* plus the `StorageBackend` contract.
|
|
10
|
+
*
|
|
11
|
+
* Also augments Fastify's types: `FastifyInstance.serverUrl` (set by
|
|
12
|
+
* `fastify.decorate` in plugin.ts, read in handlers via `request.server`) and
|
|
13
|
+
* `FastifyRequest.zcap` (set by the `parseAuthHeaders` hook).
|
|
14
|
+
*/
|
|
15
|
+
// Pull in @fastify/multipart's `FastifyRequest.file()` augmentation program-wide
|
|
16
|
+
// (the request layer calls `request.file()` without importing the plugin
|
|
17
|
+
// directly).
|
|
18
|
+
import type {} from '@fastify/multipart'
|
|
19
|
+
import type { FastifyBaseLogger, FastifyRequest } from 'fastify'
|
|
20
|
+
import type { Readable } from 'node:stream'
|
|
21
|
+
import type {
|
|
22
|
+
IDID,
|
|
23
|
+
IVerificationMethod,
|
|
24
|
+
IMultikeyMethod,
|
|
25
|
+
IZcap,
|
|
26
|
+
IRootZcap,
|
|
27
|
+
IDelegatedZcap,
|
|
28
|
+
IVerifier,
|
|
29
|
+
IPublicMultikey,
|
|
30
|
+
IMultikeyDocument,
|
|
31
|
+
IVerificationKeyPair2020
|
|
32
|
+
} from '@interop/data-integrity-core'
|
|
33
|
+
// Loader types are not re-exported from the package root (its index omits
|
|
34
|
+
// ./Loader); reach them via the ./loader subpath export.
|
|
35
|
+
import type {
|
|
36
|
+
IRemoteDocument,
|
|
37
|
+
IDocumentLoader
|
|
38
|
+
} from '@interop/data-integrity-core/loader'
|
|
39
|
+
|
|
40
|
+
import type {
|
|
41
|
+
BlindedIndexQuery,
|
|
42
|
+
BlindedIndexQueryPage
|
|
43
|
+
} from './lib/blindedIndex.js'
|
|
44
|
+
|
|
45
|
+
// The shared WAS wire model now lives in `@interop/storage-core`. Import the
|
|
46
|
+
// shapes referenced by the `StorageBackend` contract below, and re-export the
|
|
47
|
+
// whole data-model surface so the rest of the server keeps importing it from
|
|
48
|
+
// this one module.
|
|
49
|
+
import type {
|
|
50
|
+
SpaceDescription,
|
|
51
|
+
CollectionDescription,
|
|
52
|
+
CollectionSummary,
|
|
53
|
+
CollectionResourcesList,
|
|
54
|
+
ResourceMetadata,
|
|
55
|
+
ResourceMetadataCustom,
|
|
56
|
+
BackendDescriptor,
|
|
57
|
+
BackendConnectionInput,
|
|
58
|
+
BackendUsage,
|
|
59
|
+
ImportStats,
|
|
60
|
+
PolicyDocument
|
|
61
|
+
} from '@interop/storage-core'
|
|
62
|
+
|
|
63
|
+
// Surface the blinded-index query shapes referenced by the `StorageBackend`
|
|
64
|
+
// contract (`queryByBlindedIndex`) from this one module.
|
|
65
|
+
export type { BlindedIndexQuery, BlindedIndexQueryPage }
|
|
66
|
+
|
|
67
|
+
// Surface the reused @interop/data-integrity-core types from this one module.
|
|
68
|
+
export type {
|
|
69
|
+
IDID,
|
|
70
|
+
IVerificationMethod,
|
|
71
|
+
IMultikeyMethod,
|
|
72
|
+
IZcap,
|
|
73
|
+
IRootZcap,
|
|
74
|
+
IDelegatedZcap,
|
|
75
|
+
IVerifier,
|
|
76
|
+
IPublicMultikey,
|
|
77
|
+
IMultikeyDocument,
|
|
78
|
+
IVerificationKeyPair2020,
|
|
79
|
+
IRemoteDocument,
|
|
80
|
+
IDocumentLoader
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Re-export the shared WAS wire model from `@interop/storage-core`.
|
|
84
|
+
export type {
|
|
85
|
+
SpaceDescription,
|
|
86
|
+
BackendReference,
|
|
87
|
+
SpaceSummary,
|
|
88
|
+
SpaceListing,
|
|
89
|
+
CollectionSummary,
|
|
90
|
+
CollectionsList,
|
|
91
|
+
ResourceSummary,
|
|
92
|
+
CollectionResourcesList,
|
|
93
|
+
CollectionDescription,
|
|
94
|
+
CollectionEncryption,
|
|
95
|
+
CollectionEncryptionEpoch,
|
|
96
|
+
CollectionEncryptionRecipient,
|
|
97
|
+
ResourceMetadata,
|
|
98
|
+
ResourceMetadataCustom,
|
|
99
|
+
BackendDescriptor,
|
|
100
|
+
BackendConnectionPublic,
|
|
101
|
+
BackendConnectionInput,
|
|
102
|
+
BackendRegistration,
|
|
103
|
+
BackendState,
|
|
104
|
+
StorageLimit,
|
|
105
|
+
CollectionUsage,
|
|
106
|
+
BackendUsage,
|
|
107
|
+
SpaceQuotaReport,
|
|
108
|
+
PolicyDocument,
|
|
109
|
+
ImportStats,
|
|
110
|
+
Action,
|
|
111
|
+
ActionInput,
|
|
112
|
+
LinkSet,
|
|
113
|
+
LinkSetEntry
|
|
114
|
+
} from '@interop/storage-core'
|
|
115
|
+
|
|
116
|
+
/** Return shape of `getResource()`. */
|
|
117
|
+
export interface ResourceResult {
|
|
118
|
+
resourceStream: Readable
|
|
119
|
+
/** resolved content-type of the stored bytes */
|
|
120
|
+
storedResourceType: string
|
|
121
|
+
/**
|
|
122
|
+
* The Resource's current monotonic `version` -- the value behind its HTTP
|
|
123
|
+
* `ETag` strong validator (`conditional-writes` feature). Absent only for a
|
|
124
|
+
* legacy Resource written before versioning.
|
|
125
|
+
*/
|
|
126
|
+
version?: number
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Transport-neutral input to `writeResource`. The request layer resolves a
|
|
131
|
+
* Fastify request into one of these shapes (see `resolveResourceInput` in
|
|
132
|
+
* requests/resourceInput.ts) so that storage backends never depend on Fastify:
|
|
133
|
+
* - `kind: 'json'` carries the parsed JSON value in `data`.
|
|
134
|
+
* - `kind: 'binary'` carries a readable byte stream — a raw blob body, or the
|
|
135
|
+
* file extracted from a multipart upload. `declaredBytes` is the up-front size
|
|
136
|
+
* when known (a raw body's `Content-Length`), used for an early quota
|
|
137
|
+
* pre-flight; it is absent for multipart parts, whose size is unknown until
|
|
138
|
+
* the stream is consumed (the backend's streaming guard enforces the limit).
|
|
139
|
+
*
|
|
140
|
+
* In both cases `contentType` is the content-type the bytes are stored under.
|
|
141
|
+
*/
|
|
142
|
+
export type ResourceInput =
|
|
143
|
+
| { kind: 'json'; contentType: string; data: unknown }
|
|
144
|
+
| {
|
|
145
|
+
kind: 'binary'
|
|
146
|
+
contentType: string
|
|
147
|
+
stream: Readable
|
|
148
|
+
declaredBytes?: number
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The parsed auth headers attached to `request.zcap` by the `parseAuthHeaders`
|
|
153
|
+
* hook. `headers` is the signed-headers list string (not the request headers
|
|
154
|
+
* object); `created` / `expires` are stringified unix timestamps.
|
|
155
|
+
*/
|
|
156
|
+
export interface ParsedZcap {
|
|
157
|
+
keyId: string
|
|
158
|
+
headers: string
|
|
159
|
+
signature: string
|
|
160
|
+
created: string
|
|
161
|
+
expires: string
|
|
162
|
+
/** the raw `Capability-Invocation` header value */
|
|
163
|
+
invocation: string
|
|
164
|
+
/** the raw `Digest` header value (absent on bodyless requests) */
|
|
165
|
+
digest?: string
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The full persisted record for a registered `external` backend (spec
|
|
170
|
+
* "Backends"). Secret-bearing -- its `connection` carries the write-side grant
|
|
171
|
+
* material -- so it is **never** serialized to a client: only the sanitized
|
|
172
|
+
* `BackendDescriptor` projection (`sanitizeBackendRecord` in `lib/backends.ts`)
|
|
173
|
+
* is. Held in usable (plaintext, this increment) form because the server is the
|
|
174
|
+
* token custodian; the read/write split is enforced by `getBackend` being the
|
|
175
|
+
* one storage method that returns this shape.
|
|
176
|
+
*/
|
|
177
|
+
export type StoredBackendRecord = BackendDescriptor & {
|
|
178
|
+
managedBy: 'external'
|
|
179
|
+
provider: string
|
|
180
|
+
connection: BackendConnectionInput
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* A WebKMS keystore configuration (the `/kms` facet).
|
|
185
|
+
* The wire shape is protocol-fixed by `@interop/webkms-client`, minus the
|
|
186
|
+
* deliberately-dropped `meterId` / `ipAllowList` fields. Stored verbatim,
|
|
187
|
+
* full-URL `id` included, so the sequence-gated update can round-trip the
|
|
188
|
+
* config unchanged; the storage key is the id's last URL segment (the
|
|
189
|
+
* server-generated local id).
|
|
190
|
+
*/
|
|
191
|
+
export interface KeystoreConfig {
|
|
192
|
+
/** full keystore URL (`<serverUrl>/kms/keystores/<localId>`), server-assigned on create */
|
|
193
|
+
id: string
|
|
194
|
+
/** the DID that controls the keystore (authorizes every invocation on it) */
|
|
195
|
+
controller: IDID
|
|
196
|
+
/** config revision: must be 0 on create, exactly previous+1 on update */
|
|
197
|
+
sequence: number
|
|
198
|
+
/** opaque KMS module alias, echoed back; this server hard-wires 'local-v1' */
|
|
199
|
+
kmsModule: string
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The full serialized form of a WebKMS-held key, INCLUDING its secret material
|
|
204
|
+
* (`privateKeyMultibase` for the asymmetric pairs, `secret` for the symmetric
|
|
205
|
+
* keys) -- the `key` unit of a {@link KmsKeyRecord}. Field names are
|
|
206
|
+
* protocol-fixed by the webkms per-type key generators. The
|
|
207
|
+
* `controller` is deliberately NOT part of it: it is always read from the live
|
|
208
|
+
* keystore config at description time, so a controller change takes effect
|
|
209
|
+
* immediately.
|
|
210
|
+
*/
|
|
211
|
+
export interface KmsStoredKey {
|
|
212
|
+
'@context': string
|
|
213
|
+
/** full key URL (`<keystoreId>/keys/<localId>`), server-assigned on generate */
|
|
214
|
+
id: string
|
|
215
|
+
/** the webkms key type (e.g. `Ed25519VerificationKey2020`) */
|
|
216
|
+
type: string
|
|
217
|
+
publicKeyMultibase?: string
|
|
218
|
+
privateKeyMultibase?: string
|
|
219
|
+
/** symmetric key material (HMAC / AES-KW), base64url */
|
|
220
|
+
secret?: string
|
|
221
|
+
/** per-key invocation chain bound, enforced at operation time */
|
|
222
|
+
maxCapabilityChainLength?: number
|
|
223
|
+
/** verbatim description `id` override (e.g. a did:key or did:web URL) */
|
|
224
|
+
publicAlias?: string
|
|
225
|
+
/** description `id` template, expanded against the key description */
|
|
226
|
+
publicAliasTemplate?: string
|
|
227
|
+
/**
|
|
228
|
+
* At-rest ONLY: present in place of the secret fields (`privateKeyMultibase`
|
|
229
|
+
* / `secret`) when the record was written under a configured record KEK
|
|
230
|
+
* (`KMS_RECORD_KEK`; see `lib/kmsRecordCipher.ts`). The in-memory
|
|
231
|
+
* `KmsStoredKey` the KMS module operates on is always the DECRYPTED form --
|
|
232
|
+
* the decrypt seam (`decryptKeyRecord`) strips this envelope and restores the
|
|
233
|
+
* secret fields before any operation reads the key. A plaintext record (the
|
|
234
|
+
* default / unconfigured deployment) never carries it.
|
|
235
|
+
*/
|
|
236
|
+
encrypted?: KmsEncryptedEnvelope
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The at-rest envelope that replaces the secret-bearing fields of a stored
|
|
241
|
+
* key's `key` when record encryption is enabled (`KMS_RECORD_KEK`; see
|
|
242
|
+
* `lib/kmsRecordCipher.ts`). A fresh per-record content-encryption key (CEK,
|
|
243
|
+
* `A256GCM`) encrypts the serialized secret subset; the CEK is wrapped
|
|
244
|
+
* (`A256KW`) under the config-supplied KEK named by `kekId` -- the rotation
|
|
245
|
+
* seam: a record keeps the `kekId` it was written under, so a rotated-in KEK
|
|
246
|
+
* never forces a rewrite. Secrets never crossed the wire and still don't: this
|
|
247
|
+
* shape lives only on disk, never in a client projection.
|
|
248
|
+
*/
|
|
249
|
+
export interface KmsEncryptedEnvelope {
|
|
250
|
+
/** id of the KEK the CEK was wrapped under (`RecordKek.id`) */
|
|
251
|
+
kekId: string
|
|
252
|
+
/**
|
|
253
|
+
* General JWE (JSON serialization): `A256GCM` content encryption with the
|
|
254
|
+
* CEK wrapped `A256KW` under the KEK. The `protected` header is the JWE AAD.
|
|
255
|
+
*/
|
|
256
|
+
jwe: {
|
|
257
|
+
protected: string
|
|
258
|
+
recipients: Array<{
|
|
259
|
+
header?: Record<string, unknown>
|
|
260
|
+
encrypted_key: string
|
|
261
|
+
}>
|
|
262
|
+
iv: string
|
|
263
|
+
ciphertext: string
|
|
264
|
+
tag: string
|
|
265
|
+
}
|
|
266
|
+
/** the secret-subset serialization inside the JWE (only `json` this increment) */
|
|
267
|
+
encoding: 'json'
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* A record-encryption key-encryption key (KEK): a raw AES-256 key plus its
|
|
272
|
+
* derived, non-secret id (`RecordKek.id`, a one-way hash of the key material,
|
|
273
|
+
* safe to store per record). Held only in process memory (config env), never in
|
|
274
|
+
* the data tree.
|
|
275
|
+
*/
|
|
276
|
+
export interface RecordKek {
|
|
277
|
+
id: string
|
|
278
|
+
key: Buffer
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* The at-rest key-record KEK registry (config `KMS_RECORD_KEK` /
|
|
283
|
+
* `KMS_RECORD_KEKS` / `KMS_RECORD_CURRENT_KEK`): every KEK available to UNWRAP a
|
|
284
|
+
* record (keyed by `RecordKek.id`) plus `currentKekId`,
|
|
285
|
+
* the KEK that WRAPS new records. `currentKekId: null` disables encryption --
|
|
286
|
+
* new records are written plaintext -- while previously registered KEKs stay
|
|
287
|
+
* available for decrypt. Rotation is a config change (register a new KEK, repoint
|
|
288
|
+
* `currentKekId`), never a schema migration.
|
|
289
|
+
*/
|
|
290
|
+
export interface KmsRecordKekRegistry {
|
|
291
|
+
keks: Map<string, RecordKek>
|
|
292
|
+
currentKekId: string | null
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* A stored WebKMS key record (the `/kms` facet), a
|
|
297
|
+
* `{keystoreId, localId, meta, key}` shape unique on `(keystoreId, localId)`.
|
|
298
|
+
* Secret-bearing -- `key` carries the full serialized key material -- so a
|
|
299
|
+
* record is **never** serialized to a client: only the sanitized key-description
|
|
300
|
+
* projection built by the KMS module is. The storage layer treats the record as
|
|
301
|
+
* an opaque unit. At rest, `key`'s secret fields are stored plaintext by default
|
|
302
|
+
* or, when `KMS_RECORD_KEK` is configured, replaced by a `key.encrypted`
|
|
303
|
+
* envelope (see `lib/kmsRecordCipher.ts`); either way the in-memory record the
|
|
304
|
+
* KMS module operates on is the decrypted form.
|
|
305
|
+
*/
|
|
306
|
+
export interface KmsKeyRecord {
|
|
307
|
+
/** the owning keystore's local id */
|
|
308
|
+
keystoreId: string
|
|
309
|
+
/** the key's server-generated local id (the last segment of `key.id`) */
|
|
310
|
+
localId: string
|
|
311
|
+
/** server-managed timestamps (ISO 8601) */
|
|
312
|
+
meta: { created: string; updated: string }
|
|
313
|
+
key: KmsStoredKey
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* The public key-description projection of a KMS-held key, as returned by
|
|
318
|
+
* `GenerateKeyOperation` and `GET <keyId>` (never any secret field). Its `id`
|
|
319
|
+
* is the key URL, or the `publicAlias` / expanded `publicAliasTemplate` when
|
|
320
|
+
* one was set at generate time; `controller` is the live keystore controller.
|
|
321
|
+
*/
|
|
322
|
+
export interface KmsKeyDescription {
|
|
323
|
+
'@context': string
|
|
324
|
+
id: string
|
|
325
|
+
type: string
|
|
326
|
+
publicKeyMultibase?: string
|
|
327
|
+
controller: IDID
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* The object a revocation aggregates under, and the unit a revocation lookup
|
|
332
|
+
* is scoped to: a keystore (the `/kms` route family) or a Space (the WAS route
|
|
333
|
+
* families). A revocation stored under one scope has no effect on the other --
|
|
334
|
+
* a chain rooted in a Space is only ever inspected against that Space's store.
|
|
335
|
+
*/
|
|
336
|
+
export type RevocationScope = { keystoreId: string } | { spaceId: string }
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* A stored zcap revocation, a `{capability, meta}` record. Unique on
|
|
340
|
+
* `(delegator, capability.id)` within
|
|
341
|
+
* its scope (the keystore or Space it is stored under); `meta.expires` is the
|
|
342
|
+
* record's own garbage-collection horizon -- one day past the capability's
|
|
343
|
+
* `expires`, after which the capability is rejected on expiry alone and the
|
|
344
|
+
* record is prunable (the one-day margin covers clock-skew grace periods).
|
|
345
|
+
*/
|
|
346
|
+
export interface RevocationRecord {
|
|
347
|
+
/** the full revoked capability, stored verbatim */
|
|
348
|
+
capability: { id: string; expires?: string; [key: string]: unknown }
|
|
349
|
+
meta: {
|
|
350
|
+
/** the party that delegated the revoked capability (its proof creator) */
|
|
351
|
+
delegator: string
|
|
352
|
+
/**
|
|
353
|
+
* the root object the revocation aggregates under (the keystore URL, or
|
|
354
|
+
* the Space URL for a WAS-route revocation)
|
|
355
|
+
*/
|
|
356
|
+
rootTarget: string
|
|
357
|
+
/** server-managed creation timestamp (ISO 8601) */
|
|
358
|
+
created: string
|
|
359
|
+
/** GC horizon (ISO 8601); absent when the capability never expires */
|
|
360
|
+
expires?: string
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The `(capabilityId, delegator)` pair identifying one delegated capability in
|
|
366
|
+
* a chain for a revocation-store lookup.
|
|
367
|
+
*/
|
|
368
|
+
export interface CapabilitySummary {
|
|
369
|
+
capabilityId: string
|
|
370
|
+
delegator: string
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* A backend-adapter factory: given a registered (secret-bearing)
|
|
375
|
+
* `StoredBackendRecord` and a logger, returns the live `StorageBackend` that
|
|
376
|
+
* speaks to that provider. The teaching server's adapter strategy is Layer 3
|
|
377
|
+
* (not spec), so this type is server-local. Keyed by `record.provider` in the
|
|
378
|
+
* `BackendProviderRegistry`.
|
|
379
|
+
*/
|
|
380
|
+
export type BackendProvider = (
|
|
381
|
+
record: StoredBackendRecord,
|
|
382
|
+
options: { logger: FastifyBaseLogger }
|
|
383
|
+
) => StorageBackend
|
|
384
|
+
|
|
385
|
+
/** The injected provider-adapter registry, keyed by `record.provider`. */
|
|
386
|
+
export type BackendProviderRegistry = Map<string, BackendProvider>
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* The persistence contract a storage backend implements (currently
|
|
390
|
+
* `FileSystemBackend`; the port is designed to admit additional adapters). The
|
|
391
|
+
* active backend is injected into the Fastify instance via
|
|
392
|
+
* `createApp({ backend })` and read in handlers as `request.server.storage`.
|
|
393
|
+
*
|
|
394
|
+
* Invariants:
|
|
395
|
+
* - The getters resolve to a falsy value (not throw) when the target is absent;
|
|
396
|
+
* callers test `if (!description)` and translate that into a 404.
|
|
397
|
+
* - Write methods are upserts (create if absent, overwrite if present); their
|
|
398
|
+
* resolved value is implementation-defined and ignored.
|
|
399
|
+
* - Delete methods are idempotent and resolve once the target is gone.
|
|
400
|
+
* - Resources are identified by `resourceId` alone within a Collection; a
|
|
401
|
+
* Resource has exactly one current representation. `writeResource` replaces
|
|
402
|
+
* any existing representation, including one previously stored under a
|
|
403
|
+
* different content-type. `getResource`'s `contentType` is an advisory hint:
|
|
404
|
+
* single-representation backends return the one representation regardless, and
|
|
405
|
+
* the stored content-type comes back in `ResourceResult.storedResourceType`.
|
|
406
|
+
* Where the content-type lives is an adapter detail (filename segment /
|
|
407
|
+
* map-value field / future SQL column).
|
|
408
|
+
*
|
|
409
|
+
* Note: `exportSpace` resolves a tar-stream `Pack` at runtime, typed here as the
|
|
410
|
+
* `Readable` it extends (tar-stream ships no types).
|
|
411
|
+
*/
|
|
412
|
+
export interface StorageBackend {
|
|
413
|
+
/**
|
|
414
|
+
* Optional logger the backend writes diagnostics through (Fastify's pino
|
|
415
|
+
* logger, `FastifyBaseLogger`). `createApp` wires `fastify.log` here; backends
|
|
416
|
+
* default to a silent pino logger until it is set.
|
|
417
|
+
*/
|
|
418
|
+
logger?: FastifyBaseLogger
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* OPTIONAL startup hook for backends with a connection lifecycle (e.g. the
|
|
422
|
+
* Postgres backend connects and applies its schema migrations here).
|
|
423
|
+
* Awaited once by the plugin composition during registration, before the
|
|
424
|
+
* server starts listening. Backends without startup work omit it.
|
|
425
|
+
*/
|
|
426
|
+
init?(): Promise<void>
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* OPTIONAL shutdown hook (e.g. draining a connection pool). Wired to the
|
|
430
|
+
* Fastify `onClose` hook by the plugin composition. Backends without
|
|
431
|
+
* teardown work omit it.
|
|
432
|
+
*/
|
|
433
|
+
close?(): Promise<void>
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* The per-upload size cap in bytes (spec "Quotas", `maxUploadBytes`), or
|
|
437
|
+
* `undefined` for no cap. Enforced by `writeResource`; also read by the
|
|
438
|
+
* request layer to bound the in-memory buffer of a multipart file part (so an
|
|
439
|
+
* oversize multipart upload is rejected before it is fully buffered).
|
|
440
|
+
*/
|
|
441
|
+
maxUploadBytes?: number
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* The backend's self-description, as advertised at
|
|
445
|
+
* `GET /space/:spaceId/backends`. Synchronous: a backend knows its own
|
|
446
|
+
* characteristics without any I/O.
|
|
447
|
+
*/
|
|
448
|
+
describe(): BackendDescriptor
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Measures the storage the given Space consumes on this backend, for the
|
|
452
|
+
* Space Quota report (spec "Quotas"). Resolves a `BackendUsage` entry: the
|
|
453
|
+
* backend's identity plus measured `usageBytes`, derived `state`, and the
|
|
454
|
+
* configured `limit`. The per-Collection `usageByCollection` breakdown is
|
|
455
|
+
* included only when `includeCollections` is set (the spec's opt-in
|
|
456
|
+
* `?include=collections`), so a backend for which the breakdown is expensive
|
|
457
|
+
* computes it only on request. The Space is guaranteed to exist by the request
|
|
458
|
+
* layer before this is called; an absent Space dir reports zero usage.
|
|
459
|
+
*/
|
|
460
|
+
reportUsage(options: {
|
|
461
|
+
spaceId: string
|
|
462
|
+
includeCollections?: boolean
|
|
463
|
+
}): Promise<BackendUsage>
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Measures the storage a single Collection consumes on this backend, for the
|
|
467
|
+
* per-Collection Quota report (spec "Quotas",
|
|
468
|
+
* `GET /space/{id}/{cid}/quota`). Resolves a `BackendUsage` entry whose
|
|
469
|
+
* `usageBytes` is scoped to the Collection, while `state` / `limit` /
|
|
470
|
+
* `restrictedActions` describe the backend's overall condition (the quota is a
|
|
471
|
+
* per-backend limit); the per-Collection breakdown (`usageByCollection`) is
|
|
472
|
+
* omitted. OPTIONAL: a backend that cannot account per-Collection omits this
|
|
473
|
+
* method, and the request layer returns `unsupported-operation` (501). The
|
|
474
|
+
* Space and Collection are guaranteed to exist by the request layer.
|
|
475
|
+
*/
|
|
476
|
+
reportCollectionUsage?(options: {
|
|
477
|
+
spaceId: string
|
|
478
|
+
collectionId: string
|
|
479
|
+
}): Promise<BackendUsage>
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Writes a Space Description (full replacement). The server-managed
|
|
483
|
+
* `createdBy` is authoritative, never taken from `spaceDescription`: the
|
|
484
|
+
* backend drops any value carried in that (client-supplied) document and
|
|
485
|
+
* records `createdBy` from the first write's invoker, preserving it verbatim
|
|
486
|
+
* on every later write. Omitting `createdBy` on a first write leaves it
|
|
487
|
+
* unrecorded rather than letting the body supply one.
|
|
488
|
+
*/
|
|
489
|
+
writeSpace(options: {
|
|
490
|
+
spaceId: string
|
|
491
|
+
spaceDescription: SpaceDescription
|
|
492
|
+
/** DID of the invoker; recorded as `createdBy` on first write only */
|
|
493
|
+
createdBy?: IDID
|
|
494
|
+
}): Promise<void>
|
|
495
|
+
getSpaceDescription(options: {
|
|
496
|
+
spaceId: string
|
|
497
|
+
}): Promise<SpaceDescription | undefined>
|
|
498
|
+
deleteSpace(options: { spaceId: string }): Promise<void>
|
|
499
|
+
/**
|
|
500
|
+
* Enumerates every Space stored on this backend (the candidate set for the
|
|
501
|
+
* List Spaces operation; the request layer filters it down to the Spaces the
|
|
502
|
+
* caller is authorized to see). Resolves an empty array when nothing is
|
|
503
|
+
* stored yet (must not throw on an absent storage root).
|
|
504
|
+
*/
|
|
505
|
+
listSpaces(): Promise<SpaceDescription[]>
|
|
506
|
+
listCollections(options: { spaceId: string }): Promise<CollectionSummary[]>
|
|
507
|
+
/**
|
|
508
|
+
* Packs the Space as a tar archive: its Collections, Resources (including
|
|
509
|
+
* tombstones), policies, metadata sidecars, and the Space's zcap revocation
|
|
510
|
+
* records (so a revoked capability stays revoked across an export/import
|
|
511
|
+
* round-trip). Backend registration records (secret material) do NOT
|
|
512
|
+
* travel.
|
|
513
|
+
*/
|
|
514
|
+
exportSpace(options: { spaceId: string }): Promise<Readable>
|
|
515
|
+
/**
|
|
516
|
+
* Merges a Space-export archive into an existing Space, skip-not-overwrite
|
|
517
|
+
* per item; the archive's revocation records are restored under this
|
|
518
|
+
* Space's scope on the same terms (already-stored records are skipped).
|
|
519
|
+
*/
|
|
520
|
+
importSpace(options: {
|
|
521
|
+
spaceId: string
|
|
522
|
+
tarStream: Readable
|
|
523
|
+
}): Promise<ImportStats>
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Writes a Collection Description (full replacement), bumping its monotonic
|
|
527
|
+
* description `version` (the `ETag` validator behind conditional Collection
|
|
528
|
+
* writes; the `key-epochs` feature) and returning the new version. `createdBy`
|
|
529
|
+
* is server-managed on the same terms as `writeSpace`'s. When `ifMatch` is
|
|
530
|
+
* supplied it is evaluated atomically with the write: the current description
|
|
531
|
+
* ETag must equal it (an update-if-unchanged compare-and-swap that keeps two
|
|
532
|
+
* concurrent recipient edits from clobbering one another), else
|
|
533
|
+
* `precondition-failed` (412). The version travels only as the `ETag` header
|
|
534
|
+
* -- it is kept OUT of the stored/wire Collection Description body.
|
|
535
|
+
*/
|
|
536
|
+
writeCollection(options: {
|
|
537
|
+
spaceId: string
|
|
538
|
+
collectionId: string
|
|
539
|
+
collectionDescription: CollectionDescription
|
|
540
|
+
/** DID of the invoker; recorded as `createdBy` on first write only */
|
|
541
|
+
createdBy?: IDID
|
|
542
|
+
ifMatch?: string
|
|
543
|
+
/**
|
|
544
|
+
* Invoked atomically with the write (inside the backend's per-Collection
|
|
545
|
+
* lock / row-locking transaction) against the freshly re-read current
|
|
546
|
+
* description (`undefined` on a create); throwing aborts the write. Carries
|
|
547
|
+
* the request layer's state-transition rails -- e.g. the epoch append-only
|
|
548
|
+
* check -- which are otherwise evaluated against a pre-lock read and could
|
|
549
|
+
* miss a concurrent write.
|
|
550
|
+
*/
|
|
551
|
+
assertTransition?: (
|
|
552
|
+
prior?: CollectionDescription & { descriptionVersion?: number }
|
|
553
|
+
) => void
|
|
554
|
+
}): Promise<{ version: number }>
|
|
555
|
+
/**
|
|
556
|
+
* Reads a Collection Description. Resolves falsy when the Collection does not
|
|
557
|
+
* exist. `descriptionVersion` is the out-of-band `ETag` validator (the handler
|
|
558
|
+
* strips it from the wire body and sets it as the `ETag` header); absent for a
|
|
559
|
+
* legacy Collection written before description versioning.
|
|
560
|
+
*/
|
|
561
|
+
getCollectionDescription(options: {
|
|
562
|
+
spaceId: string
|
|
563
|
+
collectionId: string
|
|
564
|
+
}): Promise<
|
|
565
|
+
(CollectionDescription & { descriptionVersion?: number }) | undefined
|
|
566
|
+
>
|
|
567
|
+
deleteCollection(options: {
|
|
568
|
+
spaceId: string
|
|
569
|
+
collectionId: string
|
|
570
|
+
}): Promise<void>
|
|
571
|
+
/**
|
|
572
|
+
* Lists a Collection's Resources, OPTIONALLY paginated (spec "Pagination").
|
|
573
|
+
* `limit` bounds the page (a backend MAY clamp an oversized value to its own
|
|
574
|
+
* maximum); `cursor` is the opaque token from a prior page's `next`, naming
|
|
575
|
+
* the keyset position to resume from. With neither, the first (or only) page
|
|
576
|
+
* is returned. The result carries `next` -- a ready-to-follow URL with the
|
|
577
|
+
* cursor and limit baked in -- if and only if a further page may follow; its
|
|
578
|
+
* absence marks the last page. A malformed/un-honorable `cursor` rejects with
|
|
579
|
+
* `InvalidCursorError` (400 `invalid-cursor`).
|
|
580
|
+
*
|
|
581
|
+
* `collectionDescription` (the caller's already-fetched control-plane
|
|
582
|
+
* description) supplies the listing's `name` / `type` and encryption flag; a
|
|
583
|
+
* data-plane backend selected by a Collection never holds the description
|
|
584
|
+
* itself (it lives on the control plane), so it MUST be passed in for such a
|
|
585
|
+
* backend.
|
|
586
|
+
*/
|
|
587
|
+
listCollectionItems(options: {
|
|
588
|
+
spaceId: string
|
|
589
|
+
collectionId: string
|
|
590
|
+
limit?: number
|
|
591
|
+
cursor?: string
|
|
592
|
+
collectionDescription?: CollectionDescription
|
|
593
|
+
}): Promise<CollectionResourcesList>
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Writes a Resource representation, bumping its monotonic `version` (the ETag
|
|
597
|
+
* validator), and returns the new version. When a conditional-write
|
|
598
|
+
* precondition is supplied (`conditional-writes` feature) it is evaluated
|
|
599
|
+
* atomically with the write: `ifMatch` is an update-if-unchanged (the current
|
|
600
|
+
* ETag must equal it), `ifNoneMatch` is a create-if-absent (`If-None-Match:
|
|
601
|
+
* *`); a mismatch rejects with `precondition-failed` (412).
|
|
602
|
+
*
|
|
603
|
+
* A backend carrying the `blinded-index-query` feature also enforces the EDV
|
|
604
|
+
* unique-attribute invariant on JSON writes: an `indexed` blinded attribute
|
|
605
|
+
* marked `unique: true` whose (HMAC key id, name, value) triple is already
|
|
606
|
+
* claimed by another live document in the same Collection rejects with
|
|
607
|
+
* `UniqueAttributeConflictError` (409), evaluated atomically with the write
|
|
608
|
+
* (see `lib/blindedIndex.ts`). Conflicts require `unique: true` on both
|
|
609
|
+
* sides, and a document keeping its own unique attribute across an update
|
|
610
|
+
* never self-conflicts.
|
|
611
|
+
*/
|
|
612
|
+
writeResource(options: {
|
|
613
|
+
spaceId: string
|
|
614
|
+
collectionId: string
|
|
615
|
+
resourceId: string
|
|
616
|
+
input: ResourceInput
|
|
617
|
+
/**
|
|
618
|
+
* DID of the party whose capability invocation authorized this write (the
|
|
619
|
+
* signing key's DID, fragment stripped). Recorded as the Resource's
|
|
620
|
+
* server-managed `createdBy` on the FIRST write and preserved verbatim by
|
|
621
|
+
* every later write, exactly as `createdAt` is -- so it names the creator,
|
|
622
|
+
* not the last writer. Omitted by callers with no resolved invoker (a
|
|
623
|
+
* direct backend call), in which case no `createdBy` is recorded.
|
|
624
|
+
*/
|
|
625
|
+
createdBy?: IDID
|
|
626
|
+
/**
|
|
627
|
+
* The client-declared key epoch this content was encrypted under (the
|
|
628
|
+
* `key-epochs` feature). Stored opaquely on the Resource's metadata and
|
|
629
|
+
* returned by reads; a content write with no epoch CLEARS any stored stamp
|
|
630
|
+
* (the new ciphertext's epoch is unknown). The server never computes or
|
|
631
|
+
* verifies it.
|
|
632
|
+
*/
|
|
633
|
+
epoch?: string
|
|
634
|
+
ifMatch?: string
|
|
635
|
+
ifNoneMatch?: boolean
|
|
636
|
+
}): Promise<{ version: number }>
|
|
637
|
+
getResource(options: {
|
|
638
|
+
spaceId: string
|
|
639
|
+
collectionId: string
|
|
640
|
+
resourceId: string
|
|
641
|
+
/** advisory hint only; single-representation backends ignore it for lookup */
|
|
642
|
+
contentType?: string
|
|
643
|
+
}): Promise<ResourceResult>
|
|
644
|
+
/**
|
|
645
|
+
* Deletes a Resource. When `ifMatch` is supplied (`conditional-writes`), the
|
|
646
|
+
* delete proceeds only if the Resource's current ETag matches, evaluated
|
|
647
|
+
* atomically with the removal; a mismatch rejects with `precondition-failed`
|
|
648
|
+
* (412). Without it, the delete is unconditional and idempotent.
|
|
649
|
+
*/
|
|
650
|
+
deleteResource(options: {
|
|
651
|
+
spaceId: string
|
|
652
|
+
collectionId: string
|
|
653
|
+
resourceId: string
|
|
654
|
+
ifMatch?: string
|
|
655
|
+
}): Promise<void>
|
|
656
|
+
/**
|
|
657
|
+
* Reads a Resource's Metadata object. `version` / `metaVersion` are the
|
|
658
|
+
* out-of-band ETag validators (the handler strips them from the wire body);
|
|
659
|
+
* the Metadata's own `createdBy` rides along in it.
|
|
660
|
+
*/
|
|
661
|
+
getResourceMetadata(options: {
|
|
662
|
+
spaceId: string
|
|
663
|
+
collectionId: string
|
|
664
|
+
resourceId: string
|
|
665
|
+
}): Promise<
|
|
666
|
+
(ResourceMetadata & { version?: number; metaVersion?: number }) | undefined
|
|
667
|
+
>
|
|
668
|
+
/**
|
|
669
|
+
* Replaces the user-writable `custom` object of a Resource's Metadata (full
|
|
670
|
+
* replacement; pass `{}` to clear). Resolves `undefined` when the Resource
|
|
671
|
+
* does not exist (this operation does not create one) so the handler can 404,
|
|
672
|
+
* else the Resource's new `metaVersion` (the `/meta` ETag validator, bumped
|
|
673
|
+
* on each metadata write independently of the content `version`).
|
|
674
|
+
*
|
|
675
|
+
* On an encrypted Collection `custom` is the opaque encryption envelope (an
|
|
676
|
+
* arbitrary JSON object) rather than a `{ name, tags }` object; the backend
|
|
677
|
+
* stores it verbatim. When `ifMatch` / `ifNoneMatch` is supplied
|
|
678
|
+
* (`conditional-writes`), the write is gated on the current `metaVersion`
|
|
679
|
+
* atomically, rejecting a mismatch with `precondition-failed` (412).
|
|
680
|
+
*/
|
|
681
|
+
writeResourceMetadata(options: {
|
|
682
|
+
spaceId: string
|
|
683
|
+
collectionId: string
|
|
684
|
+
resourceId: string
|
|
685
|
+
custom: ResourceMetadataCustom | Record<string, unknown>
|
|
686
|
+
/**
|
|
687
|
+
* The client-declared key epoch (the `key-epochs` feature), a sibling of
|
|
688
|
+
* `custom`. Unlike `custom` (full replacement), an OMITTED `epoch`
|
|
689
|
+
* PRESERVES the stored value -- the stamp describes the content write, not
|
|
690
|
+
* the metadata write -- while a supplied value replaces it. Stored opaquely.
|
|
691
|
+
*/
|
|
692
|
+
epoch?: string
|
|
693
|
+
ifMatch?: string
|
|
694
|
+
ifNoneMatch?: boolean
|
|
695
|
+
}): Promise<{ metaVersion: number } | undefined>
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* OPTIONAL replication change feed (the `changes` query profile.
|
|
699
|
+
* Returns the Collection's JSON-document
|
|
700
|
+
* Resources and tombstones changed strictly after `checkpoint`, in change
|
|
701
|
+
* order (`(updatedAt, resourceId)` ascending), capped at `limit` (a backend
|
|
702
|
+
* MAY clamp an oversized value to its own maximum). With no `checkpoint`, the
|
|
703
|
+
* feed starts from the beginning.
|
|
704
|
+
*
|
|
705
|
+
* Each document carries its monotonic content `version`, its `metaVersion`
|
|
706
|
+
* (when a metadata write has occurred), `updatedAt`, the server-managed
|
|
707
|
+
* `createdBy` (the creator's DID, when one was recorded -- so provenance
|
|
708
|
+
* replicates and does not have to be fetched per Resource from `/meta`), and
|
|
709
|
+
* -- so metadata replicates alongside content -- the user-writable `custom`
|
|
710
|
+
* object (the opaque encryption envelope on an encrypted Collection). A
|
|
711
|
+
* tombstone keeps its `createdBy`, as it keeps its `createdAt`. A
|
|
712
|
+
* metadata-only edit
|
|
713
|
+
* re-surfaces the Resource with a bumped `updatedAt` / `metaVersion` but its
|
|
714
|
+
* `version` / `data` unchanged. A tombstone (soft-deleted Resource) is
|
|
715
|
+
* surfaced with `deleted: true` and no `data` so the delete replicates until
|
|
716
|
+
* clients catch up. Binary (non-JSON) Resources are excluded -- attachment
|
|
717
|
+
* replication is future work. The result's
|
|
718
|
+
* `checkpoint` is the `{ id, updatedAt }` of the last returned document (the
|
|
719
|
+
* keyset position a follow-up call resumes after), or `null` when nothing
|
|
720
|
+
* changed since `checkpoint`.
|
|
721
|
+
*
|
|
722
|
+
* OPTIONAL: a backend that omits this method does not serve the change feed,
|
|
723
|
+
* and the request layer returns `unsupported-operation` (501). The Space and
|
|
724
|
+
* Collection are guaranteed to exist by the request layer.
|
|
725
|
+
*/
|
|
726
|
+
changesSince?(options: {
|
|
727
|
+
spaceId: string
|
|
728
|
+
collectionId: string
|
|
729
|
+
checkpoint?: { id: string; updatedAt: string }
|
|
730
|
+
limit: number
|
|
731
|
+
}): Promise<{
|
|
732
|
+
documents: Array<{
|
|
733
|
+
resourceId: string
|
|
734
|
+
version: number
|
|
735
|
+
metaVersion?: number
|
|
736
|
+
createdBy?: IDID
|
|
737
|
+
updatedAt: string
|
|
738
|
+
deleted: boolean
|
|
739
|
+
data?: unknown
|
|
740
|
+
// Omitted when unset, never `null`: the handler projects this straight
|
|
741
|
+
// onto the wire `ChangeDocument.custom`, which admits no null.
|
|
742
|
+
custom?: ResourceMetadataCustom | Record<string, unknown>
|
|
743
|
+
/**
|
|
744
|
+
* The client-declared key epoch the Resource was encrypted under (the
|
|
745
|
+
* `key-epochs` feature), when one was stamped. Rides the feed so a
|
|
746
|
+
* replicating reader picks the right epoch key without a `/meta` fetch.
|
|
747
|
+
*/
|
|
748
|
+
epoch?: string
|
|
749
|
+
}>
|
|
750
|
+
checkpoint: { id: string; updatedAt: string } | null
|
|
751
|
+
}>
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* OPTIONAL blinded-index query (the `blinded-index` query profile; the
|
|
755
|
+
* `blinded-index-query` feature token). Evaluates an EDV query -- `equals`
|
|
756
|
+
* (OR across elements of an AND within each element's blinded `{name:
|
|
757
|
+
* value}` pairs) or `has` (every named blinded attribute present) -- against
|
|
758
|
+
* the HMAC-blinded `indexed` entries of the Collection's live JSON
|
|
759
|
+
* documents, scoped to the `query.index` HMAC key id. Matching is opaque
|
|
760
|
+
* string comparison; the backend performs no cryptography. With `count`,
|
|
761
|
+
* resolves only the match total; otherwise a page of the matching stored
|
|
762
|
+
* documents verbatim, in ascending `resourceId` order, paginated with the
|
|
763
|
+
* standard opaque cursor (`cursor` present iff `hasMore`; a malformed one
|
|
764
|
+
* rejects with `invalid-cursor` 400). Both first-party backends answer
|
|
765
|
+
* through `lib/blindedIndex.ts` so semantics cannot drift.
|
|
766
|
+
*
|
|
767
|
+
* OPTIONAL: a backend that omits this method does not serve the profile,
|
|
768
|
+
* and the request layer returns `unsupported-operation` (501). The Space and
|
|
769
|
+
* Collection are guaranteed to exist by the request layer.
|
|
770
|
+
*/
|
|
771
|
+
queryByBlindedIndex?(options: {
|
|
772
|
+
spaceId: string
|
|
773
|
+
collectionId: string
|
|
774
|
+
query: BlindedIndexQuery
|
|
775
|
+
count?: boolean
|
|
776
|
+
limit?: number
|
|
777
|
+
cursor?: string
|
|
778
|
+
}): Promise<{ count: number } | BlindedIndexQueryPage>
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Access-control policy documents. The level is selected by which ids are
|
|
782
|
+
* present: Space (`spaceId`), Collection (`+ collectionId`), or Resource
|
|
783
|
+
* (`+ collectionId + resourceId`). Getters resolve falsy when absent.
|
|
784
|
+
*/
|
|
785
|
+
getPolicy(options: {
|
|
786
|
+
spaceId: string
|
|
787
|
+
collectionId?: string
|
|
788
|
+
resourceId?: string
|
|
789
|
+
}): Promise<PolicyDocument | undefined>
|
|
790
|
+
writePolicy(options: {
|
|
791
|
+
spaceId: string
|
|
792
|
+
collectionId?: string
|
|
793
|
+
resourceId?: string
|
|
794
|
+
policy: PolicyDocument
|
|
795
|
+
}): Promise<void>
|
|
796
|
+
deletePolicy(options: {
|
|
797
|
+
spaceId: string
|
|
798
|
+
collectionId?: string
|
|
799
|
+
resourceId?: string
|
|
800
|
+
}): Promise<void>
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Registered `external` backend records (spec "Backends"). The read/write
|
|
804
|
+
* asymmetry is the secret boundary: `getBackend` is the only method that
|
|
805
|
+
* returns the secret-bearing `StoredBackendRecord`; `listBackends` returns
|
|
806
|
+
* sanitized `BackendDescriptor`s. A registered backend is listed but not yet
|
|
807
|
+
* selectable as a Collection's `backend` this increment (the live adapter is
|
|
808
|
+
* future work).
|
|
809
|
+
*/
|
|
810
|
+
writeBackend(options: {
|
|
811
|
+
spaceId: string
|
|
812
|
+
backendId: string
|
|
813
|
+
record: StoredBackendRecord
|
|
814
|
+
}): Promise<void>
|
|
815
|
+
/** The full (secret-bearing) record, or `undefined` when absent. Internal use. */
|
|
816
|
+
getBackend(options: {
|
|
817
|
+
spaceId: string
|
|
818
|
+
backendId: string
|
|
819
|
+
}): Promise<StoredBackendRecord | undefined>
|
|
820
|
+
/** The Space's registered external backends, **sanitized** (no secrets). */
|
|
821
|
+
listBackends(options: { spaceId: string }): Promise<BackendDescriptor[]>
|
|
822
|
+
/** Idempotent: no error when the record is absent. */
|
|
823
|
+
deleteBackend(options: { spaceId: string; backendId: string }): Promise<void>
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* WebKMS keystore configs (the `/kms` facet).
|
|
827
|
+
* Keystores are a sibling tree to Spaces (`data/keystores/<localId>/`),
|
|
828
|
+
* keyed by `keystoreId` -- the server-generated *local* id, i.e. the last
|
|
829
|
+
* segment of the config's full-URL `id`. The protocol defines no keystore
|
|
830
|
+
* delete.
|
|
831
|
+
*
|
|
832
|
+
* Writes a keystore config unconditionally (the create path; local ids are
|
|
833
|
+
* server-generated 128-bit random values, so create never collides). The
|
|
834
|
+
* sequence-gated update path is `updateKeystore`.
|
|
835
|
+
*/
|
|
836
|
+
writeKeystore(options: {
|
|
837
|
+
keystoreId: string
|
|
838
|
+
config: KeystoreConfig
|
|
839
|
+
}): Promise<void>
|
|
840
|
+
getKeystore(options: {
|
|
841
|
+
keystoreId: string
|
|
842
|
+
}): Promise<KeystoreConfig | undefined>
|
|
843
|
+
/**
|
|
844
|
+
* Replaces a keystore config if and only if, atomically with the write:
|
|
845
|
+
* the keystore exists, `config.sequence` is exactly the stored sequence + 1,
|
|
846
|
+
* and `config.kmsModule` matches the stored one (the module is immutable).
|
|
847
|
+
* Otherwise rejects with the protocol's 409 state conflict
|
|
848
|
+
* (`KeystoreStateConflictError`) -- one merged conflict kind.
|
|
849
|
+
*/
|
|
850
|
+
updateKeystore(options: {
|
|
851
|
+
keystoreId: string
|
|
852
|
+
config: KeystoreConfig
|
|
853
|
+
}): Promise<void>
|
|
854
|
+
/**
|
|
855
|
+
* Every stored keystore config whose `controller` matches, sorted by local
|
|
856
|
+
* id (the request layer caps the wire result). Resolves an empty array when
|
|
857
|
+
* nothing is stored yet (must not throw on an absent storage root).
|
|
858
|
+
*/
|
|
859
|
+
listKeystoresByController(options: {
|
|
860
|
+
controller: IDID
|
|
861
|
+
}): Promise<KeystoreConfig[]>
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* WebKMS key records, stored under their keystore
|
|
865
|
+
* (`data/keystores/<keystoreId>/keys/<localId>.json`), unique on
|
|
866
|
+
* `(keystoreId, localId)`. The record is opaque to the storage layer -- the
|
|
867
|
+
* at-rest record cipher (`KMS_RECORD_KEK`, `lib/kmsRecordCipher.ts`) applies
|
|
868
|
+
* above the backend, at the KMS orchestration seam, so no schema change is
|
|
869
|
+
* needed here. The protocol defines no key delete or update -- a record is
|
|
870
|
+
* immutable once inserted.
|
|
871
|
+
*
|
|
872
|
+
* Inserts a key record, create-only: rejects with the protocol's 409
|
|
873
|
+
* duplicate conflict (`KeyIdConflictError`) when a record already exists at
|
|
874
|
+
* `(keystoreId, localId)`, atomically with the write.
|
|
875
|
+
*/
|
|
876
|
+
insertKey(options: {
|
|
877
|
+
keystoreId: string
|
|
878
|
+
localId: string
|
|
879
|
+
record: KmsKeyRecord
|
|
880
|
+
}): Promise<void>
|
|
881
|
+
getKey(options: {
|
|
882
|
+
keystoreId: string
|
|
883
|
+
localId: string
|
|
884
|
+
}): Promise<KmsKeyRecord | undefined>
|
|
885
|
+
/**
|
|
886
|
+
* Every stored key record under the keystore, sorted by local id (the
|
|
887
|
+
* request layer caps and paginates the wire result). The record is opaque to
|
|
888
|
+
* storage -- the at-rest cipher applies above the backend (as for `getKey`),
|
|
889
|
+
* so records come back exactly as stored. Resolves an empty array when the
|
|
890
|
+
* keystore has no keys yet (must not throw on an absent keys directory /
|
|
891
|
+
* table).
|
|
892
|
+
*/
|
|
893
|
+
listKeys(options: {
|
|
894
|
+
keystoreId: string
|
|
895
|
+
}): Promise<Array<{ localId: string; record: KmsKeyRecord }>>
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* ZCap revocations, stored under their scope -- a keystore
|
|
899
|
+
* (`data/keystores/<keystoreId>/revocations/`) or a Space
|
|
900
|
+
* (`data/space-revocations/<spaceId>/`, kept out of the Space tree so a
|
|
901
|
+
* revocation directory can never be mistaken for, or collide with, a
|
|
902
|
+
* Collection). Unique on `(delegator, capability.id)` within the scope.
|
|
903
|
+
* Neither protocol defines a revocation read or delete: records exist only
|
|
904
|
+
* to be consulted by the chain-inspection hook, and lapse via
|
|
905
|
+
* `meta.expires` (the capability is rejected on its own expiry from then
|
|
906
|
+
* on). Deleting a Space deletes its revocations with it.
|
|
907
|
+
*
|
|
908
|
+
* Inserts a revocation record, create-only: rejects with the protocol's 409
|
|
909
|
+
* duplicate (`DuplicateRevocationError`) when a record already exists at
|
|
910
|
+
* `(meta.delegator, capability.id)`, atomically with the write. The scope
|
|
911
|
+
* must exist: inserting under an absent keystore or Space rejects with
|
|
912
|
+
* `StorageError` (the request layer 404-masks unknown scopes long before
|
|
913
|
+
* this; the Postgres backend's foreign keys enforce the same at the store).
|
|
914
|
+
*/
|
|
915
|
+
insertRevocation(options: {
|
|
916
|
+
scope: RevocationScope
|
|
917
|
+
record: RevocationRecord
|
|
918
|
+
}): Promise<void>
|
|
919
|
+
/**
|
|
920
|
+
* True when any of the given capabilities has a stored, unexpired
|
|
921
|
+
* revocation under the scope. Records past their `meta.expires` GC
|
|
922
|
+
* horizon count as not revoked (the capability itself has expired) and may
|
|
923
|
+
* be pruned on the way through.
|
|
924
|
+
*/
|
|
925
|
+
isRevoked(options: {
|
|
926
|
+
scope: RevocationScope
|
|
927
|
+
capabilities: CapabilitySummary[]
|
|
928
|
+
}): Promise<boolean>
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* Decision returned by an {@link AuthorizeProvisioning} callback for a
|
|
933
|
+
* provisioning request (`POST /spaces/` or `POST /kms/keystores`):
|
|
934
|
+
* - `verify` -- proceed with normal zcap capability-invocation verification;
|
|
935
|
+
* - `grant` -- the callback itself authorized the request (e.g. a valid
|
|
936
|
+
* onboarding token); skip zcap verification for this request;
|
|
937
|
+
* - `deny` -- refuse provisioning (403).
|
|
938
|
+
*/
|
|
939
|
+
export type ProvisioningDecision = 'verify' | 'grant' | 'deny'
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Provisioning gate callback: decides whether a request to one of the two open
|
|
943
|
+
* provisioning endpoints (`POST /spaces/`, `POST /kms/keystores`) may proceed.
|
|
944
|
+
* May instead throw a `ProblemError` subclass to return a custom status/body.
|
|
945
|
+
* @param options {object}
|
|
946
|
+
* @param options.request {import('fastify').FastifyRequest} the provisioning request
|
|
947
|
+
* @returns {ProvisioningDecision | Promise<ProvisioningDecision>}
|
|
948
|
+
*/
|
|
949
|
+
export type AuthorizeProvisioning = (options: {
|
|
950
|
+
request: FastifyRequest
|
|
951
|
+
}) => ProvisioningDecision | Promise<ProvisioningDecision>
|
|
952
|
+
|
|
953
|
+
declare module 'fastify' {
|
|
954
|
+
interface FastifyInstance {
|
|
955
|
+
serverUrl: string
|
|
956
|
+
storage: StorageBackend
|
|
957
|
+
/**
|
|
958
|
+
* The provider-adapter registry: maps a registered backend's `provider` to
|
|
959
|
+
* the factory that builds its live `StorageBackend` adapter. Read by the
|
|
960
|
+
* resolver (lib/backendRegistry.ts). Empty in production this stage (no
|
|
961
|
+
* real adapter yet); injected in tests. Set by `fastify.decorate` in
|
|
962
|
+
* plugin.ts.
|
|
963
|
+
*/
|
|
964
|
+
backendProviders: BackendProviderRegistry
|
|
965
|
+
/**
|
|
966
|
+
* The optional server-wide registration allowlist: the backend `provider`
|
|
967
|
+
* names a client may register (config `WAS_ENABLED_BACKENDS`). `undefined`
|
|
968
|
+
* means no allowlist -- any provider may be registered (permissive
|
|
969
|
+
* default).
|
|
970
|
+
*/
|
|
971
|
+
enabledBackendProviders?: string[]
|
|
972
|
+
/**
|
|
973
|
+
* The at-rest key-record encryption registry (config `KMS_RECORD_KEK` /
|
|
974
|
+
* `KMS_RECORD_KEKS` / `KMS_RECORD_CURRENT_KEK`): the KEK(s) available to
|
|
975
|
+
* unwrap stored WebKMS key records plus the
|
|
976
|
+
* `currentKekId` selecting the one that wraps NEW records. `undefined` (or
|
|
977
|
+
* `currentKekId: null`) means encryption is disabled -- records are written
|
|
978
|
+
* plaintext (the teaching default). Read at the KMS orchestration seam
|
|
979
|
+
* (`KeyRequest`), never inside a backend (records stay opaque to storage).
|
|
980
|
+
* Set by `fastify.decorate` in plugin.ts.
|
|
981
|
+
*/
|
|
982
|
+
kmsRecordKek?: KmsRecordKekRegistry
|
|
983
|
+
/**
|
|
984
|
+
* The optional provisioning gate for the two open provisioning endpoints
|
|
985
|
+
* (`POST /spaces/`, `POST /kms/keystores`). `undefined` means allow (the
|
|
986
|
+
* teaching default -- anyone may provision by proving control of the body's
|
|
987
|
+
* controller DID). Set by `fastify.decorate` in plugin.ts, either from the
|
|
988
|
+
* `authorizeProvisioning` option or the built-in onboarding-token check.
|
|
989
|
+
*/
|
|
990
|
+
authorizeProvisioning?: AuthorizeProvisioning
|
|
991
|
+
}
|
|
992
|
+
interface FastifyRequest {
|
|
993
|
+
/**
|
|
994
|
+
* Set by the provisioning gate when a request to a provisioning endpoint
|
|
995
|
+
* was authorized by the configured provisioning policy (e.g. a valid
|
|
996
|
+
* onboarding token) instead of a capability invocation. When set, the auth
|
|
997
|
+
* and digest hooks and the handler's controller-consent check are skipped
|
|
998
|
+
* (the request carries a Bearer token, not an HTTP Signature).
|
|
999
|
+
*/
|
|
1000
|
+
provisioningAuthorized?: boolean
|
|
1001
|
+
/**
|
|
1002
|
+
* Set by the `parseAuthHeaders` hook when auth headers are present. Absent
|
|
1003
|
+
* for anonymous reads (the `requireAuthHeaders` hook lets safe methods
|
|
1004
|
+
* through without auth so a fallback policy can grant access).
|
|
1005
|
+
*/
|
|
1006
|
+
zcap?: ParsedZcap
|
|
1007
|
+
/**
|
|
1008
|
+
* The exact request body bytes, captured by the `captureRawBody`
|
|
1009
|
+
* preParsing hook for JSON/text bodies so `verifyBodyDigest` can recompute
|
|
1010
|
+
* the `Digest` header against what the client signed (re-serializing the
|
|
1011
|
+
* parsed body is not guaranteed byte-identical). Absent for streamed
|
|
1012
|
+
* (multipart / tar) bodies, which are left unbuffered.
|
|
1013
|
+
*/
|
|
1014
|
+
rawBody?: Buffer
|
|
1015
|
+
}
|
|
1016
|
+
}
|