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
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default configuration: spec and FEP documentation URLs referenced by the
|
|
3
|
+
* home page, error responses, and space-export manifests.
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { parseKekMultibase } from './lib/kmsRecordCipher.js';
|
|
8
|
+
// package.json sits a level above both src/ (dev, via tsx) and dist/ (prod),
|
|
9
|
+
// so '../package.json' from import.meta.dirname resolves in either layout.
|
|
10
|
+
const packageJsonPath = path.join(import.meta.dirname, '..', 'package.json');
|
|
11
|
+
/** Server version, read from package.json at startup. */
|
|
12
|
+
export const SERVER_VERSION = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')).version;
|
|
13
|
+
/**
|
|
14
|
+
* Space Description cache (see src/requests/spaceContext.ts). The description is
|
|
15
|
+
* read on every authorized handler, so it is memoized per storage backend.
|
|
16
|
+
* Writes invalidate the entry explicitly; the short TTL is a backstop that also
|
|
17
|
+
* bounds staleness when several server processes share one storage backend (so
|
|
18
|
+
* one process's cache cannot serve another process's write indefinitely).
|
|
19
|
+
*/
|
|
20
|
+
export const SPACE_DESCRIPTION_CACHE_TTL = 5_000; // milliseconds
|
|
21
|
+
/** Max number of Space Descriptions held per backend cache (LRU-bounded). */
|
|
22
|
+
export const SPACE_DESCRIPTION_CACHE_MAX = 1_000;
|
|
23
|
+
/**
|
|
24
|
+
* Max number of resolved external-backend adapters held per provider-registry
|
|
25
|
+
* cache (see src/lib/backendRegistry.ts), LRU-bounded. One adapter instance is
|
|
26
|
+
* memoized per selected `{spaceId}/{backendId}` and reused across requests;
|
|
27
|
+
* record changes bust the entry explicitly (no TTL backstop is needed because
|
|
28
|
+
* the provider registry itself is fixed for an instance's lifetime).
|
|
29
|
+
*/
|
|
30
|
+
export const RESOLVED_BACKEND_CACHE_MAX = 1_000;
|
|
31
|
+
/**
|
|
32
|
+
* Linkset relation URI for the access-control `policy` auxiliary resource
|
|
33
|
+
* (RFC9264 linkset discovery; see src/policy.ts and the linkset handlers).
|
|
34
|
+
*/
|
|
35
|
+
export const POLICY_LINK_RELATION = 'https://wallet.storage/spec#policy';
|
|
36
|
+
/**
|
|
37
|
+
* Linkset relation URI for a Collection's selected `backend` auxiliary resource
|
|
38
|
+
* (RFC9264 linkset discovery; advertised at `/space/{id}/{cid}/backend`).
|
|
39
|
+
*/
|
|
40
|
+
export const BACKEND_LINK_RELATION = 'https://wallet.storage/spec#backend';
|
|
41
|
+
/**
|
|
42
|
+
* Linkset relation URI for a Collection's `quota` report auxiliary resource
|
|
43
|
+
* (RFC9264 linkset discovery; advertised at `/space/{id}/{cid}/quota`).
|
|
44
|
+
*/
|
|
45
|
+
export const QUOTA_LINK_RELATION = 'https://wallet.storage/spec#quota';
|
|
46
|
+
/**
|
|
47
|
+
* Linkset relation URI for a Space's `backends-available` auxiliary resource
|
|
48
|
+
* (RFC9264 linkset discovery; advertised at `/space/{id}/backends`).
|
|
49
|
+
*/
|
|
50
|
+
export const BACKENDS_AVAILABLE_LINK_RELATION = 'https://wallet.storage/spec#backends-available';
|
|
51
|
+
/**
|
|
52
|
+
* Linkset relation URI for a Space's `quotas` report auxiliary resource
|
|
53
|
+
* (RFC9264 linkset discovery; advertised at `/space/{id}/quotas`).
|
|
54
|
+
*/
|
|
55
|
+
export const QUOTAS_LINK_RELATION = 'https://wallet.storage/spec#quotas';
|
|
56
|
+
/**
|
|
57
|
+
* Fraction of a backend's configured capacity at or above which its quota
|
|
58
|
+
* report `state` becomes `near-limit` (spec "Quotas"). Below this it is `ok`;
|
|
59
|
+
* at or above full capacity it is `over-quota`. Only applies when a finite
|
|
60
|
+
* capacity is configured (an unlimited backend is always `ok`).
|
|
61
|
+
*/
|
|
62
|
+
export const QUOTA_NEAR_LIMIT_FRACTION = 0.9;
|
|
63
|
+
/**
|
|
64
|
+
* Write-path quota usage cache TTL (see
|
|
65
|
+
* `FileSystemBackend._assertSpaceHeadroom`). The quota pre-flight measures a
|
|
66
|
+
* Space's on-disk usage with `du`, which walks the whole Space tree -- too
|
|
67
|
+
* costly to repeat on every resource write. The measured total is cached per
|
|
68
|
+
* Space for this long, with each accepted write's incoming bytes added to the
|
|
69
|
+
* cached figure; deletes invalidate the entry. The TTL bounds the drift of
|
|
70
|
+
* that optimistic accounting (overwrites that replaced rather than added
|
|
71
|
+
* bytes, streamed bodies of undeclared size) -- the quota is a documented
|
|
72
|
+
* soft limit, and the TTL bounds the re-measurement window.
|
|
73
|
+
*/
|
|
74
|
+
export const QUOTA_USAGE_CACHE_TTL = 5_000; // milliseconds
|
|
75
|
+
/**
|
|
76
|
+
* The single in-process KMS module this server hard-wires.
|
|
77
|
+
* A keystore created without one gets this alias, and it is immutable thereafter.
|
|
78
|
+
*/
|
|
79
|
+
export const DEFAULT_KMS_MODULE = 'local-v1';
|
|
80
|
+
/**
|
|
81
|
+
* Max keystore configs returned by `GET /kms/keystores?controller=...` (the
|
|
82
|
+
* webkms protocol's list cap).
|
|
83
|
+
*/
|
|
84
|
+
export const KEYSTORE_LIST_LIMIT = 100;
|
|
85
|
+
/**
|
|
86
|
+
* Max key descriptions returned per page by
|
|
87
|
+
* `GET /kms/keystores/:keystoreId/keys` (the List Keys fork extension). A
|
|
88
|
+
* further page is signalled by the response's `next` cursor URL; realistic
|
|
89
|
+
* keystores hold a handful of keys, so one page is the steady state.
|
|
90
|
+
*/
|
|
91
|
+
export const KEY_LIST_LIMIT = 100;
|
|
92
|
+
/**
|
|
93
|
+
* Max capability delegation chain length accepted on `/kms` invocations, the
|
|
94
|
+
* root capability included (webkms-switch's `maxChainLength` default). A
|
|
95
|
+
* per-key `maxCapabilityChainLength` (1-10, set at generate time) may narrow
|
|
96
|
+
* this further for key operations; it can never widen it.
|
|
97
|
+
*/
|
|
98
|
+
export const KMS_MAX_CHAIN_LENGTH = 10;
|
|
99
|
+
/**
|
|
100
|
+
* Max time-to-live of a delegated capability accepted on `/kms` invocations,
|
|
101
|
+
* measured `expires` minus the delegation proof's `created`, in milliseconds
|
|
102
|
+
* (90 days -- webkms-switch's `maxDelegationTtl` default). One unified bound
|
|
103
|
+
* for every `/kms` route family:
|
|
104
|
+
* revocation plus the mandatory `expires` is the real control.
|
|
105
|
+
*/
|
|
106
|
+
export const KMS_MAX_DELEGATION_TTL = 90 * 24 * 60 * 60 * 1000;
|
|
107
|
+
/** TCP port the server listens on when `PORT` is unset. */
|
|
108
|
+
export const DEFAULT_PORT = 3002;
|
|
109
|
+
/**
|
|
110
|
+
* The default per-upload byte cap applied by BOTH backends when
|
|
111
|
+
* `MAX_UPLOAD_BYTES` is unset (a default-on limit, 64 MiB). Every blob write
|
|
112
|
+
* buffers through process memory on at least one path (the Postgres single
|
|
113
|
+
* `bytea`, multipart's in-memory `toBuffer()`), so "no cap" would be a footgun.
|
|
114
|
+
* Opt out explicitly with `MAX_UPLOAD_BYTES=unlimited`, which the backends
|
|
115
|
+
* accept only where an unbounded upload is actually safe (the filesystem
|
|
116
|
+
* streaming path; the Postgres backend rejects it at construction).
|
|
117
|
+
*/
|
|
118
|
+
export const DEFAULT_MAX_UPLOAD_BYTES = 64 * 1024 * 1024;
|
|
119
|
+
/**
|
|
120
|
+
* Default cap on the number of Spaces a single controller may create, applied
|
|
121
|
+
* by BOTH backends when `MAX_SPACES_PER_CONTROLLER` is unset (a default-on
|
|
122
|
+
* count quota). Chosen far above any teaching or conformance workload while
|
|
123
|
+
* still bounding runaway creation by one controller. Opt out with
|
|
124
|
+
* `MAX_SPACES_PER_CONTROLLER=unlimited`.
|
|
125
|
+
*/
|
|
126
|
+
export const DEFAULT_MAX_SPACES_PER_CONTROLLER = 100;
|
|
127
|
+
/**
|
|
128
|
+
* Default cap on the number of Collections a single Space may hold, applied by
|
|
129
|
+
* BOTH backends when `MAX_COLLECTIONS_PER_SPACE` is unset (a default-on count
|
|
130
|
+
* quota). Opt out with `MAX_COLLECTIONS_PER_SPACE=unlimited`.
|
|
131
|
+
*/
|
|
132
|
+
export const DEFAULT_MAX_COLLECTIONS_PER_SPACE = 100;
|
|
133
|
+
/**
|
|
134
|
+
* Default cap on the number of live Resources a single Space may hold across
|
|
135
|
+
* all its Collections, applied by BOTH backends when `MAX_RESOURCES_PER_SPACE`
|
|
136
|
+
* is unset (a default-on count quota). A tombstone (soft-deleted Resource) does
|
|
137
|
+
* not count against it. Opt out with `MAX_RESOURCES_PER_SPACE=unlimited`.
|
|
138
|
+
*/
|
|
139
|
+
export const DEFAULT_MAX_RESOURCES_PER_SPACE = 10_000;
|
|
140
|
+
/**
|
|
141
|
+
* Reads and validates the server's whole env config surface in one place
|
|
142
|
+
* (fail-fast startup): a missing `SERVER_URL` or any malformed value throws
|
|
143
|
+
* with the offending variable named, before the server starts listening --
|
|
144
|
+
* instead of silently breaking ZCap matching at request time.
|
|
145
|
+
* @param [env] {NodeJS.ProcessEnv} defaults to `process.env`
|
|
146
|
+
* @returns {EnvConfig}
|
|
147
|
+
*/
|
|
148
|
+
export function loadConfigFromEnv(env = process.env) {
|
|
149
|
+
return {
|
|
150
|
+
serverUrl: parseServerUrl(env.SERVER_URL),
|
|
151
|
+
port: parsePort(env.PORT),
|
|
152
|
+
databaseUrl: parseDatabaseUrl(env.DATABASE_URL),
|
|
153
|
+
storageLimitPerSpace: parseStorageLimit(env.STORAGE_LIMIT_PER_SPACE),
|
|
154
|
+
maxUploadBytes: parseMaxUploadBytes(env.MAX_UPLOAD_BYTES),
|
|
155
|
+
maxSpacesPerController: parseCountLimit(env.MAX_SPACES_PER_CONTROLLER, 'MAX_SPACES_PER_CONTROLLER'),
|
|
156
|
+
maxCollectionsPerSpace: parseCountLimit(env.MAX_COLLECTIONS_PER_SPACE, 'MAX_COLLECTIONS_PER_SPACE'),
|
|
157
|
+
maxResourcesPerSpace: parseCountLimit(env.MAX_RESOURCES_PER_SPACE, 'MAX_RESOURCES_PER_SPACE'),
|
|
158
|
+
enabledBackendProviders: parseEnabledBackends(env.WAS_ENABLED_BACKENDS),
|
|
159
|
+
kmsRecordKek: parseKmsRecordKekRegistry({
|
|
160
|
+
kek: env.KMS_RECORD_KEK,
|
|
161
|
+
keks: env.KMS_RECORD_KEKS,
|
|
162
|
+
currentKek: env.KMS_RECORD_CURRENT_KEK
|
|
163
|
+
}),
|
|
164
|
+
onboardingToken: parseOnboardingToken(env.WAS_ONBOARDING_TOKEN)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Validates a server base URL (the `serverUrl` option / `SERVER_URL` env
|
|
169
|
+
* value): it must be an absolute `http:`/`https:` URL with no path, query, or
|
|
170
|
+
* fragment. ZCap `invocationTarget` URLs and `Location` headers are built by
|
|
171
|
+
* resolving absolute paths against this base (`new URL(path, serverUrl)`),
|
|
172
|
+
* which silently drops any base path -- so a sub-path deployment would break
|
|
173
|
+
* every delegated invocation. Rejected at startup instead (fail-fast).
|
|
174
|
+
* @param serverUrl {string} the candidate base URL
|
|
175
|
+
* @returns {void} throws on an invalid value
|
|
176
|
+
*/
|
|
177
|
+
export function assertValidServerUrl(serverUrl) {
|
|
178
|
+
let url;
|
|
179
|
+
try {
|
|
180
|
+
url = new URL(serverUrl);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
throw new Error(`serverUrl (env SERVER_URL) must be an absolute URL; got "${serverUrl}".`);
|
|
184
|
+
}
|
|
185
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
186
|
+
throw new Error(`serverUrl (env SERVER_URL) must use http: or https:; got "${serverUrl}".`);
|
|
187
|
+
}
|
|
188
|
+
if (url.pathname !== '/' || url.search !== '' || url.hash !== '') {
|
|
189
|
+
throw new Error(`serverUrl (env SERVER_URL) must not include a path, query, or ` +
|
|
190
|
+
`fragment -- deploying under a sub-path is not supported; ` +
|
|
191
|
+
`got "${serverUrl}".`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Parses the `SERVER_URL` env value: the server's base URL, used to build and
|
|
196
|
+
* match ZCap `invocationTarget` URLs (host and port must match the client's
|
|
197
|
+
* exactly). Required -- unset would silently break all ZCap matching, so
|
|
198
|
+
* startup fails instead. The value is trimmed but otherwise preserved
|
|
199
|
+
* byte-for-byte (never normalized), since capability targets compare as exact
|
|
200
|
+
* strings. Validated by {@link assertValidServerUrl}.
|
|
201
|
+
* @param raw {string|undefined} the raw env value
|
|
202
|
+
* @returns {string} the trimmed, validated base URL
|
|
203
|
+
*/
|
|
204
|
+
export function parseServerUrl(raw) {
|
|
205
|
+
if (raw === undefined || raw.trim() === '') {
|
|
206
|
+
throw new Error(`SERVER_URL is required: the server base URL used to build and match ` +
|
|
207
|
+
`ZCap invocationTarget URLs (e.g. SERVER_URL='http://localhost:3002').`);
|
|
208
|
+
}
|
|
209
|
+
const serverUrl = raw.trim();
|
|
210
|
+
assertValidServerUrl(serverUrl);
|
|
211
|
+
return serverUrl;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Parses the `PORT` env value into the TCP port to listen on. An unset or
|
|
215
|
+
* empty value returns {@link DEFAULT_PORT}.
|
|
216
|
+
* @param raw {string|undefined} the raw env value
|
|
217
|
+
* @returns {number} the port
|
|
218
|
+
*/
|
|
219
|
+
export function parsePort(raw) {
|
|
220
|
+
if (raw === undefined || raw.trim() === '') {
|
|
221
|
+
return DEFAULT_PORT;
|
|
222
|
+
}
|
|
223
|
+
const value = Number(raw);
|
|
224
|
+
if (!Number.isInteger(value) || value < 1 || value > 65535) {
|
|
225
|
+
throw new Error(`PORT must be an integer between 1 and 65535; got "${raw}".`);
|
|
226
|
+
}
|
|
227
|
+
return value;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Parses the `DATABASE_URL` env value: a Postgres connection string that,
|
|
231
|
+
* when set, selects the PostgreSQL storage backend (unset keeps the default
|
|
232
|
+
* filesystem backend). The string's shape is left to the `pg` driver, which
|
|
233
|
+
* accepts several connection-string forms; an unset or empty value returns
|
|
234
|
+
* `undefined`.
|
|
235
|
+
* @param raw {string|undefined} the raw env value
|
|
236
|
+
* @returns {string|undefined} the trimmed connection string, or `undefined`
|
|
237
|
+
*/
|
|
238
|
+
export function parseDatabaseUrl(raw) {
|
|
239
|
+
if (raw === undefined || raw.trim() === '') {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
return raw.trim();
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Parses the at-rest key-record encryption env surface into a
|
|
246
|
+
* {@link KmsRecordKekRegistry} (the optional hardening increment). Each KEK is a
|
|
247
|
+
* single AES-256 key-encryption key in base58btc Multikey form
|
|
248
|
+
* (`secretKeyMultibase`, header `0xa2 0x01`), whose id is derived from its
|
|
249
|
+
* material (`deriveKekId`) and stored per record -- so registering a second KEK
|
|
250
|
+
* for rotation is a config change, not a schema migration. Three env variables
|
|
251
|
+
* feed it:
|
|
252
|
+
*
|
|
253
|
+
* - `KMS_RECORD_KEK` -- a single KEK (the teaching default alias);
|
|
254
|
+
* - `KMS_RECORD_KEKS` -- a comma-separated list of KEKs, each registered by its
|
|
255
|
+
* derived id; the FIRST entry is the current KEK by default, so a rotation is
|
|
256
|
+
* "prepend the new KEK, keep the old one behind it". Surrounding whitespace is
|
|
257
|
+
* trimmed and empty entries ignored; a duplicate entry (same derived id twice)
|
|
258
|
+
* throws;
|
|
259
|
+
* - `KMS_RECORD_CURRENT_KEK` -- optionally overrides which registered KEK wraps
|
|
260
|
+
* NEW records: a `urn:kek:sha256:<hex>` id, a multibase KEK value (its id
|
|
261
|
+
* derived), or the literal `none` (case-insensitive) which sets
|
|
262
|
+
* `currentKekId: null` -- the decrypt-only posture (old records still read, new
|
|
263
|
+
* records are written plaintext). It must name a registered KEK.
|
|
264
|
+
*
|
|
265
|
+
* All three unset/empty returns `undefined`, meaning encryption is disabled --
|
|
266
|
+
* key records are written plaintext (the default, honest about the teaching
|
|
267
|
+
* server's threat model). Setting both `KMS_RECORD_KEK` and `KMS_RECORD_KEKS` is
|
|
268
|
+
* ambiguous and throws; setting `KMS_RECORD_CURRENT_KEK` with no KEK configured
|
|
269
|
+
* throws. A malformed value throws (fails startup), naming the offending
|
|
270
|
+
* variable (and, for a list entry, its 1-based position) but never echoing the
|
|
271
|
+
* secret.
|
|
272
|
+
* @param options {object}
|
|
273
|
+
* @param [options.kek] {string} the `KMS_RECORD_KEK` value
|
|
274
|
+
* @param [options.keks] {string} the `KMS_RECORD_KEKS` value
|
|
275
|
+
* @param [options.currentKek] {string} the `KMS_RECORD_CURRENT_KEK` value
|
|
276
|
+
* @returns {KmsRecordKekRegistry|undefined} the registry, or `undefined` when
|
|
277
|
+
* no KEK is configured
|
|
278
|
+
*/
|
|
279
|
+
export function parseKmsRecordKekRegistry({ kek, keks, currentKek }) {
|
|
280
|
+
const kekSet = kek !== undefined && kek.trim() !== '';
|
|
281
|
+
const keksSet = keks !== undefined && keks.trim() !== '';
|
|
282
|
+
const currentSet = currentKek !== undefined && currentKek.trim() !== '';
|
|
283
|
+
if (kekSet && keksSet) {
|
|
284
|
+
throw new Error('Set only one of KMS_RECORD_KEK or KMS_RECORD_KEKS, not both (ambiguous).');
|
|
285
|
+
}
|
|
286
|
+
// Build the ordered registry: each KEK keyed by its derived id, in config
|
|
287
|
+
// order (the first entry is the default current KEK).
|
|
288
|
+
const registry = new Map();
|
|
289
|
+
const order = [];
|
|
290
|
+
if (kekSet) {
|
|
291
|
+
const parsed = parseKekMultibase(kek.trim());
|
|
292
|
+
registry.set(parsed.id, parsed);
|
|
293
|
+
order.push(parsed);
|
|
294
|
+
}
|
|
295
|
+
else if (keksSet) {
|
|
296
|
+
let position = 0;
|
|
297
|
+
for (const entry of keks.split(',')) {
|
|
298
|
+
const trimmed = entry.trim();
|
|
299
|
+
if (trimmed === '') {
|
|
300
|
+
continue; // empty entries are ignored (like WAS_ENABLED_BACKENDS)
|
|
301
|
+
}
|
|
302
|
+
position += 1;
|
|
303
|
+
const parsed = parseKekMultibase(trimmed, `KMS_RECORD_KEKS entry ${position}`);
|
|
304
|
+
if (registry.has(parsed.id)) {
|
|
305
|
+
throw new Error(`KMS_RECORD_KEKS entry ${position} duplicates an earlier KEK ` +
|
|
306
|
+
`(same derived kekId); register each KEK only once.`);
|
|
307
|
+
}
|
|
308
|
+
registry.set(parsed.id, parsed);
|
|
309
|
+
order.push(parsed);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// No KEK material at all (all unset, or a list of only empty entries): a
|
|
313
|
+
// dangling KMS_RECORD_CURRENT_KEK is an error; otherwise encryption is off.
|
|
314
|
+
if (registry.size === 0) {
|
|
315
|
+
if (currentSet) {
|
|
316
|
+
throw new Error('KMS_RECORD_CURRENT_KEK is set but no KEK is configured; ' +
|
|
317
|
+
'set KMS_RECORD_KEK or KMS_RECORD_KEKS.');
|
|
318
|
+
}
|
|
319
|
+
return undefined;
|
|
320
|
+
}
|
|
321
|
+
const currentKekId = currentSet
|
|
322
|
+
? resolveCurrentKekId(currentKek.trim(), registry)
|
|
323
|
+
: order[0].id;
|
|
324
|
+
return { keks: registry, currentKekId };
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Resolves a `KMS_RECORD_CURRENT_KEK` value to a registered `kekId` (or `null`
|
|
328
|
+
* for the literal `none`, the decrypt-only posture). The value is either a
|
|
329
|
+
* `urn:kek:sha256:<hex>` id or a multibase KEK whose id is derived; either way
|
|
330
|
+
* it must match a KEK already in `registry`. Throws on an unregistered target,
|
|
331
|
+
* naming the variable but never echoing the secret.
|
|
332
|
+
* @param value {string} the trimmed `KMS_RECORD_CURRENT_KEK` value
|
|
333
|
+
* @param registry {Map<string, RecordKek>} the registered KEKs, by id
|
|
334
|
+
* @returns {string|null} the resolved current `kekId`, or `null` for `none`
|
|
335
|
+
*/
|
|
336
|
+
function resolveCurrentKekId(value, registry) {
|
|
337
|
+
if (value.toLowerCase() === 'none') {
|
|
338
|
+
return null; // decrypt-only: keep KEKs for unwrap, write new records plaintext
|
|
339
|
+
}
|
|
340
|
+
const kekId = value.startsWith('urn:kek:sha256:')
|
|
341
|
+
? value
|
|
342
|
+
: parseKekMultibase(value, 'KMS_RECORD_CURRENT_KEK').id;
|
|
343
|
+
if (!registry.has(kekId)) {
|
|
344
|
+
throw new Error('KMS_RECORD_CURRENT_KEK names a KEK that is not registered ' +
|
|
345
|
+
'(no matching KMS_RECORD_KEK / KMS_RECORD_KEKS entry).');
|
|
346
|
+
}
|
|
347
|
+
return kekId;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Parses the `STORAGE_LIMIT_PER_SPACE` env value into a per-Space capacity in
|
|
351
|
+
* bytes (spec "Quotas"). Accepts a plain non-negative integer number of bytes,
|
|
352
|
+
* or the literal `unlimited` (case-insensitive, trimmed) which returns
|
|
353
|
+
* `Infinity` -- an explicitly acknowledged unlimited quota (no startup
|
|
354
|
+
* warning). An unset or empty value returns `undefined`, meaning no limit is
|
|
355
|
+
* configured (still unlimited, but `start.ts` warns to prompt an explicit
|
|
356
|
+
* choice). A malformed value throws.
|
|
357
|
+
* @param raw {string|undefined} the raw env value
|
|
358
|
+
* @returns {number|undefined} capacity in bytes, `Infinity` for `unlimited`,
|
|
359
|
+
* or `undefined` when unset
|
|
360
|
+
*/
|
|
361
|
+
export function parseStorageLimit(raw) {
|
|
362
|
+
if (raw === undefined || raw.trim() === '') {
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
if (raw.trim().toLowerCase() === 'unlimited') {
|
|
366
|
+
return Infinity;
|
|
367
|
+
}
|
|
368
|
+
const value = Number(raw);
|
|
369
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
370
|
+
throw new Error(`STORAGE_LIMIT_PER_SPACE must be a non-negative integer number of ` +
|
|
371
|
+
`bytes, or "unlimited"; got "${raw}".`);
|
|
372
|
+
}
|
|
373
|
+
return value;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Parses the `MAX_UPLOAD_BYTES` env value into a per-upload size cap in bytes
|
|
377
|
+
* (spec "Quotas", the backend's `maxUploadBytes` constraint). Accepts a plain
|
|
378
|
+
* non-negative integer number of bytes, or the literal `unlimited`
|
|
379
|
+
* (case-insensitive, trimmed) which returns `Infinity` -- explicitly no cap.
|
|
380
|
+
* An unset or empty value returns `undefined`, meaning not configured: the
|
|
381
|
+
* backends apply the {@link DEFAULT_MAX_UPLOAD_BYTES} default (a default-on
|
|
382
|
+
* limit). A single upload exceeding the cap is rejected with
|
|
383
|
+
* `payload-too-large` (413), while smaller uploads still succeed. A malformed
|
|
384
|
+
* value throws.
|
|
385
|
+
* @param raw {string|undefined} the raw env value
|
|
386
|
+
* @returns {number|undefined} the per-upload cap in bytes, `Infinity` for
|
|
387
|
+
* `unlimited`, or `undefined` when unset
|
|
388
|
+
*/
|
|
389
|
+
export function parseMaxUploadBytes(raw) {
|
|
390
|
+
if (raw === undefined || raw.trim() === '') {
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
if (raw.trim().toLowerCase() === 'unlimited') {
|
|
394
|
+
return Infinity;
|
|
395
|
+
}
|
|
396
|
+
const value = Number(raw);
|
|
397
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
398
|
+
throw new Error(`MAX_UPLOAD_BYTES must be a non-negative integer number of bytes, or ` +
|
|
399
|
+
`"unlimited"; got "${raw}".`);
|
|
400
|
+
}
|
|
401
|
+
return value;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Parses one of the count-quota env values (`MAX_SPACES_PER_CONTROLLER`,
|
|
405
|
+
* `MAX_COLLECTIONS_PER_SPACE`, `MAX_RESOURCES_PER_SPACE`) into a maximum count.
|
|
406
|
+
* Accepts a plain non-negative integer, or the literal `unlimited`
|
|
407
|
+
* (case-insensitive, trimmed) which returns `Infinity` -- explicitly no cap. An
|
|
408
|
+
* unset or empty value returns `undefined`, meaning not configured: the backends
|
|
409
|
+
* apply the matching default-on limit ({@link DEFAULT_MAX_SPACES_PER_CONTROLLER}
|
|
410
|
+
* and friends). A malformed value throws, naming the variable and the
|
|
411
|
+
* `unlimited` escape hatch.
|
|
412
|
+
* @param raw {string|undefined} the raw env value
|
|
413
|
+
* @param name {string} the env variable name, for the error message
|
|
414
|
+
* @returns {number|undefined} the max count, `Infinity` for `unlimited`, or
|
|
415
|
+
* `undefined` when unset
|
|
416
|
+
*/
|
|
417
|
+
export function parseCountLimit(raw, name) {
|
|
418
|
+
if (raw === undefined || raw.trim() === '') {
|
|
419
|
+
return undefined;
|
|
420
|
+
}
|
|
421
|
+
if (raw.trim().toLowerCase() === 'unlimited') {
|
|
422
|
+
return Infinity;
|
|
423
|
+
}
|
|
424
|
+
const value = Number(raw);
|
|
425
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
426
|
+
throw new Error(`${name} must be a non-negative integer, or "unlimited"; got "${raw}".`);
|
|
427
|
+
}
|
|
428
|
+
return value;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Normalizes a count-quota constructor option to the internal instance field
|
|
432
|
+
* both backends store: an unset option (`undefined`) applies the default-on
|
|
433
|
+
* `fallback` limit; a non-finite option (`Infinity`, from an explicit
|
|
434
|
+
* `unlimited`) becomes `undefined` (no cap); a finite value passes through.
|
|
435
|
+
* Shared so the two backends cannot drift on the mapping (the same posture as
|
|
436
|
+
* the `maxUploadBytes` normalization each backend already applies), letting each
|
|
437
|
+
* count guard keep its plain `!== undefined` test.
|
|
438
|
+
* @param value {number|undefined} the constructor option
|
|
439
|
+
* @param fallback {number} the default-on limit applied when unset
|
|
440
|
+
* @returns {number|undefined} the internal limit, or `undefined` for no cap
|
|
441
|
+
*/
|
|
442
|
+
export function normalizeCountLimit(value, fallback) {
|
|
443
|
+
if (value === undefined) {
|
|
444
|
+
return fallback;
|
|
445
|
+
}
|
|
446
|
+
return Number.isFinite(value) ? value : undefined;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Parses the `WAS_ENABLED_BACKENDS` env value into the server-wide registration
|
|
450
|
+
* allowlist: the backend `provider` names a client may register (spec
|
|
451
|
+
* "Backends"). A comma-separated list (e.g. `gdrive,s3`); surrounding whitespace
|
|
452
|
+
* and empty entries are ignored. An unset or empty value returns `undefined`,
|
|
453
|
+
* meaning no allowlist is configured -- any provider may be registered (the
|
|
454
|
+
* permissive default, preserving prior behavior). When set, a registration whose
|
|
455
|
+
* `provider` is not listed is rejected with `unsupported-backend` (409).
|
|
456
|
+
* @param raw {string|undefined} the raw env value
|
|
457
|
+
* @returns {string[]|undefined} the allowed provider names, or `undefined`
|
|
458
|
+
*/
|
|
459
|
+
export function parseEnabledBackends(raw) {
|
|
460
|
+
if (raw === undefined) {
|
|
461
|
+
return undefined;
|
|
462
|
+
}
|
|
463
|
+
const providers = raw
|
|
464
|
+
.split(',')
|
|
465
|
+
.map(entry => entry.trim())
|
|
466
|
+
.filter(entry => entry.length > 0);
|
|
467
|
+
return providers.length > 0 ? providers : undefined;
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Parses the `WAS_ONBOARDING_TOKEN` env value into the shared-secret onboarding
|
|
471
|
+
* token gating the two open provisioning endpoints (`POST /spaces/`,
|
|
472
|
+
* `POST /kms/keystores`). An unset or empty/whitespace-only value returns
|
|
473
|
+
* `undefined`, meaning the feature is off -- provisioning is authorized by
|
|
474
|
+
* proving control of the body's controller DID (the teaching default). When
|
|
475
|
+
* set, those two endpoints instead require an `Authorization: Bearer <token>`
|
|
476
|
+
* header matching this value, which then substitutes for zcap verification.
|
|
477
|
+
* @param raw {string|undefined} the raw env value
|
|
478
|
+
* @returns {string|undefined} the trimmed token, or `undefined` when unset
|
|
479
|
+
*/
|
|
480
|
+
export function parseOnboardingToken(raw) {
|
|
481
|
+
if (raw === undefined || raw.trim() === '') {
|
|
482
|
+
return undefined;
|
|
483
|
+
}
|
|
484
|
+
return raw.trim();
|
|
485
|
+
}
|
|
486
|
+
export const SPEC_URL = 'https://digitalcredentials.github.io/wallet-attached-storage-spec/';
|
|
487
|
+
export const UBC_MANIFEST_URL = 'https://codeberg.org/fediverse/fep/src/branch/main/fep/6fcd/fep-6fcd.md#manifest-file';
|
|
488
|
+
export const SPACE_URL = 'https://digitalcredentials.github.io/wallet-attached-storage-spec/#spaces';
|
|
489
|
+
export const COLLECTION_URL = 'https://digitalcredentials.github.io/wallet-attached-storage-spec/#collection-data-model';
|
|
490
|
+
export const RESOURCE_URL = 'https://digitalcredentials.github.io/wallet-attached-storage-spec/#resource-data-model';
|
|
491
|
+
export const POLICY_URL = 'https://digitalcredentials.github.io/wallet-attached-storage-spec/#policy';
|
|
492
|
+
export const META_URL = 'https://digitalcredentials.github.io/wallet-attached-storage-spec/#resource-metadata-data-model';
|
|
493
|
+
//# sourceMappingURL=config.default.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.default.js","sourceRoot":"","sources":["../src/config.default.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAG5D,6EAA6E;AAC7E,2EAA2E;AAC3E,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,CAAA;AAE5E,yDAAyD;AACzD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CACtC,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CACzC,CAAC,OAAiB,CAAA;AAEnB;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAA,CAAC,eAAe;AAChE,6EAA6E;AAC7E,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAA;AAEhD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,KAAK,CAAA;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,oCAAoC,CAAA;AAExE;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,qCAAqC,CAAA;AAE1E;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,mCAAmC,CAAA;AAEtE;;;GAGG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAC3C,gDAAgD,CAAA;AAElD;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,oCAAoC,CAAA;AAExE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAA;AAE5C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAA,CAAC,eAAe;AAE1D;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAA;AAE5C;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAA;AAEtC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAA;AAEjC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAEtC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAE9D,2DAA2D;AAC3D,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAA;AAEhC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAA;AAExD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,GAAG,CAAA;AAEpD;;;;GAIG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,GAAG,CAAA;AAEpD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,MAAM,CAAA;AAyDrD;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAyB,OAAO,CAAC,GAAG;IAEpC,OAAO;QACL,SAAS,EAAE,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC;QACzC,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;QACzB,WAAW,EAAE,gBAAgB,CAAC,GAAG,CAAC,YAAY,CAAC;QAC/C,oBAAoB,EAAE,iBAAiB,CAAC,GAAG,CAAC,uBAAuB,CAAC;QACpE,cAAc,EAAE,mBAAmB,CAAC,GAAG,CAAC,gBAAgB,CAAC;QACzD,sBAAsB,EAAE,eAAe,CACrC,GAAG,CAAC,yBAAyB,EAC7B,2BAA2B,CAC5B;QACD,sBAAsB,EAAE,eAAe,CACrC,GAAG,CAAC,yBAAyB,EAC7B,2BAA2B,CAC5B;QACD,oBAAoB,EAAE,eAAe,CACnC,GAAG,CAAC,uBAAuB,EAC3B,yBAAyB,CAC1B;QACD,uBAAuB,EAAE,oBAAoB,CAAC,GAAG,CAAC,oBAAoB,CAAC;QACvE,YAAY,EAAE,yBAAyB,CAAC;YACtC,GAAG,EAAE,GAAG,CAAC,cAAc;YACvB,IAAI,EAAE,GAAG,CAAC,eAAe;YACzB,UAAU,EAAE,GAAG,CAAC,sBAAsB;SACvC,CAAC;QACF,eAAe,EAAE,oBAAoB,CAAC,GAAG,CAAC,oBAAoB,CAAC;KAChE,CAAA;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,SAAiB;IACpD,IAAI,GAAQ,CAAA;IACZ,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAA;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,4DAA4D,SAAS,IAAI,CAC1E,CAAA;IACH,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CACb,6DAA6D,SAAS,IAAI,CAC3E,CAAA;IACH,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,IAAI,GAAG,CAAC,IAAI,KAAK,EAAE,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CACb,gEAAgE;YAC9D,2DAA2D;YAC3D,QAAQ,SAAS,IAAI,CACxB,CAAA;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,GAAuB;IACpD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,sEAAsE;YACpE,uEAAuE,CAC1E,CAAA;IACH,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IAC5B,oBAAoB,CAAC,SAAS,CAAC,CAAA;IAC/B,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,GAAuB;IAC/C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,OAAO,YAAY,CAAA;IACrB,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IACzB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,KAAK,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CACb,qDAAqD,GAAG,IAAI,CAC7D,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAuB;IACtD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAA;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,yBAAyB,CAAC,EACxC,GAAG,EACH,IAAI,EACJ,UAAU,EAKX;IACC,MAAM,MAAM,GAAG,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAA;IACrD,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAA;IACxD,MAAM,UAAU,GAAG,UAAU,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,CAAA;IAEvE,IAAI,MAAM,IAAI,OAAO,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,0EAA0E,CAC3E,CAAA;IACH,CAAC;IAED,0EAA0E;IAC1E,sDAAsD;IACtD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAqB,CAAA;IAC7C,MAAM,KAAK,GAAgB,EAAE,CAAA;IAE7B,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QAC7C,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAC/B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACpB,CAAC;SAAM,IAAI,OAAO,EAAE,CAAC;QACnB,IAAI,QAAQ,GAAG,CAAC,CAAA;QAChB,KAAK,MAAM,KAAK,IAAI,IAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACrC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;YAC5B,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;gBACnB,SAAQ,CAAC,wDAAwD;YACnE,CAAC;YACD,QAAQ,IAAI,CAAC,CAAA;YACb,MAAM,MAAM,GAAG,iBAAiB,CAC9B,OAAO,EACP,yBAAyB,QAAQ,EAAE,CACpC,CAAA;YACD,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CACb,yBAAyB,QAAQ,6BAA6B;oBAC5D,oDAAoD,CACvD,CAAA;YACH,CAAC;YACD,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;YAC/B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACpB,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,4EAA4E;IAC5E,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACxB,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,0DAA0D;gBACxD,wCAAwC,CAC3C,CAAA;QACH,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,MAAM,YAAY,GAAG,UAAU;QAC7B,CAAC,CAAC,mBAAmB,CAAC,UAAW,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC;QACnD,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,CAAA;IAChB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAA;AACzC,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,mBAAmB,CAC1B,KAAa,EACb,QAAgC;IAEhC,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;QACnC,OAAO,IAAI,CAAA,CAAC,kEAAkE;IAChF,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC;QAC/C,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,iBAAiB,CAAC,KAAK,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAA;IACzD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,4DAA4D;YAC1D,uDAAuD,CAC1D,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAuB;IACvD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,WAAW,EAAE,CAAC;QAC7C,OAAO,QAAQ,CAAA;IACjB,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IACzB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,mEAAmE;YACjE,+BAA+B,GAAG,IAAI,CACzC,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAuB;IAEvB,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,WAAW,EAAE,CAAC;QAC7C,OAAO,QAAQ,CAAA;IACjB,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IACzB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,sEAAsE;YACpE,qBAAqB,GAAG,IAAI,CAC/B,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAC7B,GAAuB,EACvB,IAAY;IAEZ,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,WAAW,EAAE,CAAC;QAC7C,OAAO,QAAQ,CAAA;IACjB,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IACzB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,yDAAyD,GAAG,IAAI,CACxE,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAyB,EACzB,QAAgB;IAEhB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAA;IACjB,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AACnD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,GAAuB;IAEvB,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,SAAS,GAAG,GAAG;SAClB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACpC,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAA;AACrD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,GAAuB;IAEvB,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAA;AACnB,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GACnB,oEAAoE,CAAA;AACtE,MAAM,CAAC,MAAM,gBAAgB,GAC3B,uFAAuF,CAAA;AACzF,MAAM,CAAC,MAAM,SAAS,GACpB,2EAA2E,CAAA;AAC7E,MAAM,CAAC,MAAM,cAAc,GACzB,0FAA0F,CAAA;AAC5F,MAAM,CAAC,MAAM,YAAY,GACvB,wFAAwF,CAAA;AAC1F,MAAM,CAAC,MAAM,UAAU,GACrB,2EAA2E,CAAA;AAC7E,MAAM,CAAC,MAAM,QAAQ,GACnB,iGAAiG,CAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
|
|
2
|
+
import type { LookupFunction } from 'node:net';
|
|
3
|
+
/**
|
|
4
|
+
* Builds the undici `connect.lookup` function that pins each upstream socket to
|
|
5
|
+
* the exact addresses `checkProxyTarget` already validated, keyed by hostname.
|
|
6
|
+
* The `fetch` still connects using the original hostname (so TLS certificate
|
|
7
|
+
* validation and SNI keep working) but resolves it only via this map, so a
|
|
8
|
+
* rebinding attacker cannot swap in a fresh, private address between validation
|
|
9
|
+
* and connection. A hostname absent from the map is refused (defense in depth:
|
|
10
|
+
* the agent must never resolve an unpinned host).
|
|
11
|
+
* @param pins {Map<string, {address: string, family: number}[]>} validated
|
|
12
|
+
* addresses keyed by lower-cased hostname.
|
|
13
|
+
* @returns {LookupFunction} an undici/`net`-compatible lookup callback.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createPinnedLookup(pins: Map<string, {
|
|
16
|
+
address: string;
|
|
17
|
+
family: number;
|
|
18
|
+
}[]>): LookupFunction;
|
|
19
|
+
/**
|
|
20
|
+
* Registers a server-side CORS proxy at `/api/cors`.
|
|
21
|
+
*
|
|
22
|
+
* Example:
|
|
23
|
+
* `/api/cors?url=https%3A%2F%2Fregistry.dcconsortium.org%2F.well-known%2Fopenid-federation`
|
|
24
|
+
*
|
|
25
|
+
* SSRF guard: only `http`/`https` URLs are proxied, and the target host is
|
|
26
|
+
* resolved and rejected when it maps to a private, loopback, or link-local
|
|
27
|
+
* address (e.g. `http://169.254.169.254/...` cloud-metadata, or an internal
|
|
28
|
+
* service). Redirects are followed manually (up to `MAX_REDIRECTS` hops) and
|
|
29
|
+
* every hop is re-validated the same way, so a public host cannot 3xx the
|
|
30
|
+
* proxy into an internal one. The addresses each hop validated are then pinned
|
|
31
|
+
* for the actual upstream connection (via a per-request undici `Agent` whose
|
|
32
|
+
* `connect.lookup` resolves only from that pin map), so a DNS-rebinding
|
|
33
|
+
* attacker cannot pass validation with a public IP and have the subsequent
|
|
34
|
+
* `fetch` re-resolve to a private one (closing the TOCTOU). The relayed
|
|
35
|
+
* response body is capped at `PROXY_MAX_RESPONSE_BYTES`, both from a declared
|
|
36
|
+
* `content-length` and while streaming. This is an unauthenticated open
|
|
37
|
+
* endpoint; adding a lightweight auth gate (a shared secret header, an
|
|
38
|
+
* allowlist of caller origins, or the server's own capability-invocation
|
|
39
|
+
* check) is a reasonable follow-up.
|
|
40
|
+
*
|
|
41
|
+
* @param app - Fastify instance
|
|
42
|
+
* @param _options - Fastify plugin options
|
|
43
|
+
*/
|
|
44
|
+
export declare function initCorsProxyRoutes(app: FastifyInstance, _options: FastifyPluginOptions): Promise<void>;
|
|
45
|
+
//# sourceMappingURL=corsProxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"corsProxy.d.ts","sourceRoot":"","sources":["../src/corsProxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AA8I9C;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,GACvD,cAAc,CAsBhB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,oBAAoB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAoHf"}
|