@ifc-lite/collab-server 0.2.6 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/auth.d.ts +2 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +3 -1
- package/dist/auth.js.map +1 -1
- package/dist/bin.js +29 -2
- package/dist/bin.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/layer-registry-fs.d.ts +37 -0
- package/dist/layer-registry-fs.d.ts.map +1 -0
- package/dist/layer-registry-fs.js +240 -0
- package/dist/layer-registry-fs.js.map +1 -0
- package/dist/layer-registry-route.d.ts +49 -0
- package/dist/layer-registry-route.d.ts.map +1 -0
- package/dist/layer-registry-route.js +728 -0
- package/dist/layer-registry-route.js.map +1 -0
- package/dist/layer-registry.d.ts +123 -0
- package/dist/layer-registry.d.ts.map +1 -0
- package/dist/layer-registry.js +168 -0
- package/dist/layer-registry.js.map +1 -0
- package/dist/registry-webhooks.d.ts +20 -0
- package/dist/registry-webhooks.d.ts.map +1 -0
- package/dist/registry-webhooks.js +62 -0
- package/dist/registry-webhooks.js.map +1 -0
- package/dist/room-token.d.ts +14 -0
- package/dist/room-token.d.ts.map +1 -1
- package/dist/room-token.js +30 -0
- package/dist/room-token.js.map +1 -1
- package/dist/server.d.ts +25 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +56 -0
- package/dist/server.js.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -27,8 +27,11 @@ Environment variables:
|
|
|
27
27
|
|---|---|---|
|
|
28
28
|
| `COLLAB_PORT` | `1234` | Listen port |
|
|
29
29
|
| `COLLAB_HOST` | `0.0.0.0` | Listen host |
|
|
30
|
-
| `COLLAB_DATA_DIR` | `./.collab-data` | Persistence root for room logs |
|
|
30
|
+
| `COLLAB_DATA_DIR` | `./.collab-data` | Persistence root for room logs, blobs, and the layer registry |
|
|
31
31
|
| `COLLAB_MAX_ROOMS` | `1024` | Soft cap on simultaneous rooms |
|
|
32
|
+
| `COLLAB_LAYER_REGISTRY` | off | `1`/`true` mounts the layer registry (`/api/v1/layers\|refs\|reviews`), disk-backed under `COLLAB_DATA_DIR/layer-registry` |
|
|
33
|
+
| `COLLAB_REGISTRY_WEBHOOK_URL` | off | POST registry events (layer pushed, ref moved/merged, review opened/updated/commented) to this URL |
|
|
34
|
+
| `COLLAB_REGISTRY_WEBHOOK_SECRET` | none | HMAC-SHA256 signing secret for webhook payloads (`x-ifclite-signature`) |
|
|
32
35
|
|
|
33
36
|
## Programmatic use
|
|
34
37
|
|
package/dist/auth.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export interface Principal {
|
|
|
15
15
|
meta?: Record<string, unknown>;
|
|
16
16
|
}
|
|
17
17
|
export type AuthenticateFn = (token: string | undefined, roomId: string) => Promise<Principal | null> | Principal | null;
|
|
18
|
+
/** The sentinel identity minted by `allowAnonymousEditor` — not a real credential. */
|
|
19
|
+
export declare const ANONYMOUS_USER_ID = "anonymous";
|
|
18
20
|
/** Default: anonymous editor, useful for local dev. */
|
|
19
21
|
export declare const allowAnonymousEditor: AuthenticateFn;
|
|
20
22
|
/** Default: hard-deny. Apps that don't supply an auth hook get this. */
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AAEH,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE/D,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,IAAI,CAAC;IACX,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,MAAM,cAAc,GAAG,CAC3B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,KACX,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC;AAElD,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,EAAE,cAGjC,CAAC;AAEH,wEAAwE;AACxE,eAAO,MAAM,OAAO,EAAE,cAA2B,CAAC;AAElD,iEAAiE;AACjE,wBAAgB,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAEtD"}
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AAEH,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE/D,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,IAAI,CAAC;IACX,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,MAAM,cAAc,GAAG,CAC3B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,KACX,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC;AAElD,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAE7C,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,EAAE,cAGjC,CAAC;AAEH,wEAAwE;AACxE,eAAO,MAAM,OAAO,EAAE,cAA2B,CAAC;AAElD,iEAAiE;AACjE,wBAAgB,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAEtD"}
|
package/dist/auth.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/** The sentinel identity minted by `allowAnonymousEditor` — not a real credential. */
|
|
5
|
+
export const ANONYMOUS_USER_ID = 'anonymous';
|
|
4
6
|
/** Default: anonymous editor, useful for local dev. */
|
|
5
7
|
export const allowAnonymousEditor = (_token, _room) => ({
|
|
6
|
-
userId:
|
|
8
|
+
userId: ANONYMOUS_USER_ID,
|
|
7
9
|
role: 'editor',
|
|
8
10
|
});
|
|
9
11
|
/** Default: hard-deny. Apps that don't supply an auth hook get this. */
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AA0B/D,uDAAuD;AACvD,MAAM,CAAC,MAAM,oBAAoB,GAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;IACtE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AA0B/D,sFAAsF;AACtF,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAC;AAE7C,uDAAuD;AACvD,MAAM,CAAC,MAAM,oBAAoB,GAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;IACtE,MAAM,EAAE,iBAAiB;IACzB,IAAI,EAAE,QAAQ;CACf,CAAC,CAAC;AAEH,wEAAwE;AACxE,MAAM,CAAC,MAAM,OAAO,GAAmB,GAAG,EAAE,CAAC,IAAI,CAAC;AAElD,iEAAiE;AACjE,MAAM,UAAU,QAAQ,CAAC,SAAoB;IAC3C,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,KAAK,OAAO,CAAC;AACnE,CAAC"}
|
package/dist/bin.js
CHANGED
|
@@ -7,7 +7,8 @@ import * as fs from 'node:fs';
|
|
|
7
7
|
import * as path from 'node:path';
|
|
8
8
|
import { FilePersistence, startCollabServer } from './server.js';
|
|
9
9
|
import { FsBlobStorage } from './blob-route.js';
|
|
10
|
-
import {
|
|
10
|
+
import { FsLayerRegistry } from './layer-registry-fs.js';
|
|
11
|
+
import { createRoomTokenAuthenticator, createRoomTokenRegistryAuthorizer, verifyRoomToken } from './room-token.js';
|
|
11
12
|
// `PORT` is the convention most hosts inject (Railway, Render, Fly, …).
|
|
12
13
|
const port = Number(process.env.COLLAB_PORT ?? process.env.PORT ?? 1234);
|
|
13
14
|
const host = process.env.COLLAB_HOST ?? '0.0.0.0';
|
|
@@ -16,6 +17,22 @@ const maxRooms = Number(process.env.COLLAB_MAX_ROOMS ?? 1024);
|
|
|
16
17
|
// Link-based access control is enabled by setting a signing secret. Without it
|
|
17
18
|
// the server stays open (anonymous editor) — fine for local/dev, see auth.ts.
|
|
18
19
|
const tokenSecret = process.env.COLLAB_TOKEN_SECRET;
|
|
20
|
+
// Layer registry (10-registry.md) is opt-in; when enabled it persists to the
|
|
21
|
+
// same data dir as rooms and blobs, so a mounted volume covers all three.
|
|
22
|
+
const layerRegistryEnabled = ['1', 'true'].includes((process.env.COLLAB_LAYER_REGISTRY ?? '').toLowerCase());
|
|
23
|
+
// One notification consumer via env (08-review.md §8.7); programmatic
|
|
24
|
+
// deployments pass a full webhook list through startCollabServer instead.
|
|
25
|
+
const registryWebhookUrl = process.env.COLLAB_REGISTRY_WEBHOOK_URL;
|
|
26
|
+
const registryWebhooks = registryWebhookUrl
|
|
27
|
+
? [
|
|
28
|
+
{
|
|
29
|
+
url: registryWebhookUrl,
|
|
30
|
+
...(process.env.COLLAB_REGISTRY_WEBHOOK_SECRET
|
|
31
|
+
? { secret: process.env.COLLAB_REGISTRY_WEBHOOK_SECRET }
|
|
32
|
+
: {}),
|
|
33
|
+
},
|
|
34
|
+
]
|
|
35
|
+
: [];
|
|
19
36
|
/**
|
|
20
37
|
* Accountless room access control:
|
|
21
38
|
* - Joins require a valid signed room token (role is tamper-proof + revocable).
|
|
@@ -61,6 +78,13 @@ function tokenOptions(secret, dir) {
|
|
|
61
78
|
// authorizer reuses the WS `authenticate` with a pseudo-room scope — which
|
|
62
79
|
// a room-bound token can never match. Verify signature/expiry/revocation
|
|
63
80
|
// without the room binding instead; writes additionally need editor/admin.
|
|
81
|
+
// The registry is project-scoped like blobs: verify the token without
|
|
82
|
+
// its room binding (see createRoomTokenRegistryAuthorizer) — otherwise
|
|
83
|
+
// room tokens can never reach /api/v1 and the registry is locked out.
|
|
84
|
+
authorizeRegistry: createRoomTokenRegistryAuthorizer({
|
|
85
|
+
secret,
|
|
86
|
+
isRevoked: (jti) => revoked.has(jti),
|
|
87
|
+
}),
|
|
64
88
|
authorizeBlob: (token, method) => {
|
|
65
89
|
const claims = verifyRoomToken(token ?? '', { secret });
|
|
66
90
|
if (!claims || revoked.has(claims.jti))
|
|
@@ -113,10 +137,13 @@ async function main() {
|
|
|
113
137
|
// them on restart. On a mounted volume this is durable and far cheaper.
|
|
114
138
|
blobStorage: new FsBlobStorage(dataDir),
|
|
115
139
|
maxRooms,
|
|
140
|
+
...(layerRegistryEnabled
|
|
141
|
+
? { layerRegistry: { store: new FsLayerRegistry(dataDir), webhooks: registryWebhooks } }
|
|
142
|
+
: {}),
|
|
116
143
|
...(tokenSecret ? tokenOptions(tokenSecret, dataDir) : {}),
|
|
117
144
|
});
|
|
118
145
|
// eslint-disable-next-line no-console
|
|
119
|
-
console.log(`[collab-server] listening at ${handle.url} (data: ${dataDir}, auth: ${tokenSecret ? 'room-token' : 'anonymous'})`);
|
|
146
|
+
console.log(`[collab-server] listening at ${handle.url} (data: ${dataDir}, auth: ${tokenSecret ? 'room-token' : 'anonymous'}, registry: ${layerRegistryEnabled ? 'fs' : 'off'})`);
|
|
120
147
|
const shutdown = async () => {
|
|
121
148
|
// eslint-disable-next-line no-console
|
|
122
149
|
console.log('[collab-server] shutting down…');
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;+DAE+D;AAE/D,iDAAiD;AAEjD,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAiC,MAAM,aAAa,CAAC;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,4BAA4B,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,5 +15,9 @@ export { startSecureCollabServer, type StartSecureCollabServerOptions, } from '.
|
|
|
15
15
|
export { createPathLockRegistry, harvestUpdatePaths, verifyAgainstPathLocks, type PathLock, type PathLockRegistry, } from './path-locks.js';
|
|
16
16
|
export { createRateLimiter, type RateLimitOptions, type RateLimiter, } from './rate-limit.js';
|
|
17
17
|
export { InMemoryBlobStorage, FsBlobStorage, handleBlobRequest, type ServerBlobStorage, type ServerBlobMeta, type BlobRouteOptions, type BlobAuthorizeFn, } from './blob-route.js';
|
|
18
|
-
export { signRoomToken, verifyRoomToken, createRoomTokenAuthenticator, handleTokenMintRequest, handleRevokeRequest, handleKickRequest, type RoomTokenClaims, type SecretResolver, type SignRoomTokenOptions, type VerifyRoomTokenOptions, type RoomTokenAuthenticatorOptions, type TokenEndpointOptions, type RevokeEndpointOptions, type KickEndpointOptions, type MintRequestBody, } from './room-token.js';
|
|
18
|
+
export { signRoomToken, verifyRoomToken, createRoomTokenAuthenticator, createRoomTokenRegistryAuthorizer, handleTokenMintRequest, handleRevokeRequest, handleKickRequest, type RoomTokenClaims, type SecretResolver, type SignRoomTokenOptions, type VerifyRoomTokenOptions, type RoomTokenAuthenticatorOptions, type TokenEndpointOptions, type RevokeEndpointOptions, type KickEndpointOptions, type MintRequestBody, } from './room-token.js';
|
|
19
|
+
export { assertPushableLayer, assertReportDigest, CONTENT_DIGEST_REGEX, LayerPushError, MemoryLayerRegistry, type LayerRegistryStore, type MemoryLayerRegistryLimits, type RegistryReview, type RegistryReviewDecision, type RegistryReviewStatus, type RegistryReviewTopic, } from './layer-registry.js';
|
|
20
|
+
export { FsLayerRegistry } from './layer-registry-fs.js';
|
|
21
|
+
export { emitRegistryEvent, signRegistryEvent, type RegistryWebhook, type RegistryEvent, type RegistryEventType, } from './registry-webhooks.js';
|
|
22
|
+
export { handleLayerRegistryRequest, type LayerRegistryRouteOptions, type RegistryAuthorizeFn, } from './layer-registry-route.js';
|
|
19
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EACvB,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,sBAAsB,GAC5B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,GACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,IAAI,EACT,oBAAoB,EACpB,OAAO,EACP,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,IAAI,EACJ,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,SAAS,EACT,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,yBAAyB,GAC/B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,KAAK,iBAAiB,EACtB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,cAAc,GACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,eAAe,EACf,cAAc,EACd,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,EACzB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,uBAAuB,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,uBAAuB,EACvB,KAAK,8BAA8B,GACpC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,QAAQ,EACb,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,eAAe,EACf,4BAA4B,EAC5B,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,6BAA6B,EAClC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -16,5 +16,9 @@ export { startSecureCollabServer, } from './secure-bundle.js';
|
|
|
16
16
|
export { createPathLockRegistry, harvestUpdatePaths, verifyAgainstPathLocks, } from './path-locks.js';
|
|
17
17
|
export { createRateLimiter, } from './rate-limit.js';
|
|
18
18
|
export { InMemoryBlobStorage, FsBlobStorage, handleBlobRequest, } from './blob-route.js';
|
|
19
|
-
export { signRoomToken, verifyRoomToken, createRoomTokenAuthenticator, handleTokenMintRequest, handleRevokeRequest, handleKickRequest, } from './room-token.js';
|
|
19
|
+
export { signRoomToken, verifyRoomToken, createRoomTokenAuthenticator, createRoomTokenRegistryAuthorizer, handleTokenMintRequest, handleRevokeRequest, handleKickRequest, } from './room-token.js';
|
|
20
|
+
export { assertPushableLayer, assertReportDigest, CONTENT_DIGEST_REGEX, LayerPushError, MemoryLayerRegistry, } from './layer-registry.js';
|
|
21
|
+
export { FsLayerRegistry } from './layer-registry-fs.js';
|
|
22
|
+
export { emitRegistryEvent, signRegistryEvent, } from './registry-webhooks.js';
|
|
23
|
+
export { handleLayerRegistryRequest, } from './layer-registry-route.js';
|
|
20
24
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,iBAAiB,GAIlB,MAAM,aAAa,CAAC;AAKrB,OAAO,EACL,aAAa,GAId,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,GAGjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAIL,oBAAoB,EACpB,OAAO,EACP,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,IAAI,EACJ,WAAW,GAIZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,SAAS,GAKV,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,aAAa,GAGd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,GAGf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,eAAe,EACf,cAAc,GAEf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,GAK1B,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,iBAAiB,GAElB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,uBAAuB,GAExB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,GAGvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,iBAAiB,GAGlB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GAKlB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,eAAe,EACf,4BAA4B,EAC5B,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,GAUlB,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,iBAAiB,GAIlB,MAAM,aAAa,CAAC;AAKrB,OAAO,EACL,aAAa,GAId,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,GAGjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAIL,oBAAoB,EACpB,OAAO,EACP,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,IAAI,EACJ,WAAW,GAIZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,SAAS,GAKV,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,aAAa,GAGd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,GAGf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,eAAe,EACf,cAAc,GAEf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,GAK1B,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,iBAAiB,GAElB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,uBAAuB,GAExB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,GAGvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,iBAAiB,GAGlB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GAKlB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,eAAe,EACf,4BAA4B,EAC5B,iCAAiC,EACjC,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,GAUlB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,GAOpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,GAIlB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,0BAA0B,GAG3B,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { IfcxFile } from '@ifc-lite/ifcx';
|
|
2
|
+
import type { RefEntry } from '@ifc-lite/merge';
|
|
3
|
+
import { type LayerRegistryStore, type MemoryLayerRegistryLimits, type RegistryReview } from './layer-registry.js';
|
|
4
|
+
export declare class FsLayerRegistry implements LayerRegistryStore {
|
|
5
|
+
private readonly layersDir;
|
|
6
|
+
private readonly reviewsDir;
|
|
7
|
+
private readonly reportsDir;
|
|
8
|
+
private readonly refsFile;
|
|
9
|
+
private readonly refs;
|
|
10
|
+
private readonly reviews;
|
|
11
|
+
private layerCount;
|
|
12
|
+
private reportCount;
|
|
13
|
+
private readonly maxLayers;
|
|
14
|
+
private readonly maxRefs;
|
|
15
|
+
private readonly maxReviews;
|
|
16
|
+
private readonly maxReports;
|
|
17
|
+
constructor(dataDir: string, limits?: MemoryLayerRegistryLimits);
|
|
18
|
+
private hydrateRefs;
|
|
19
|
+
private hydrateReviews;
|
|
20
|
+
private listLayerFiles;
|
|
21
|
+
private layerFile;
|
|
22
|
+
private writeAtomic;
|
|
23
|
+
push(file: IfcxFile): string;
|
|
24
|
+
hasLayer(layerId: string): boolean;
|
|
25
|
+
listLayers(): string[];
|
|
26
|
+
loadLayer(layerId: string): IfcxFile;
|
|
27
|
+
storeLayer(file: IfcxFile): string;
|
|
28
|
+
getRef(name: string): RefEntry | undefined;
|
|
29
|
+
setRef(name: string, entry: RefEntry): void;
|
|
30
|
+
listRefs(): Record<string, RefEntry>;
|
|
31
|
+
getReview(id: string): RegistryReview | undefined;
|
|
32
|
+
listReviews(): RegistryReview[];
|
|
33
|
+
putReview(review: RegistryReview): void;
|
|
34
|
+
putReport(digest: string, bytes: Uint8Array): string;
|
|
35
|
+
getReport(digest: string): Uint8Array | undefined;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=layer-registry-fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-registry-fs.d.ts","sourceRoot":"","sources":["../src/layer-registry-fs.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAIL,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAQ7B,qBAAa,eAAgB,YAAW,kBAAkB;IACxD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA+B;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,yBAA8B;IAmBnE,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,WAAW;IAiBnB,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM;IA+B5B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAKlC,UAAU,IAAI,MAAM,EAAE;IAMtB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ;IAYpC,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM;IAMlC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAK1C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI;IAe3C,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC;IAMpC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAKjD,WAAW,IAAI,cAAc,EAAE;IAI/B,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAYvC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM;IAcpD,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;CAUlD"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Disk-backed layer registry — the durable counterpart to
|
|
6
|
+
* `MemoryLayerRegistry`, for deployments with a mounted volume (the same
|
|
7
|
+
* `COLLAB_DATA_DIR` that backs `FilePersistence` and `FsBlobStorage`).
|
|
8
|
+
*
|
|
9
|
+
* Layout under `<dataDir>/layer-registry/`:
|
|
10
|
+
* layers/<blake3-hex>.json one file per content-addressed layer
|
|
11
|
+
* refs.json the whole ref record (names are arbitrary
|
|
12
|
+
* strings, so they never become filenames)
|
|
13
|
+
* reviews/<id>.json one file per review object
|
|
14
|
+
*
|
|
15
|
+
* Layers live on disk only and are read on demand: keeping every layer in
|
|
16
|
+
* RAM is exactly the hosting-cost/durability trade the blob route already
|
|
17
|
+
* rejected. Refs and reviews are small and gate merges, so they hydrate
|
|
18
|
+
* into memory at boot and write through on every change.
|
|
19
|
+
*
|
|
20
|
+
* Writes are synchronous (atomic temp + rename): the `LayerRegistryStore`
|
|
21
|
+
* interface is sync, registry operations are rare, payloads are bounded by
|
|
22
|
+
* the route's `maxBytes`, and a registry must be durable before it acks —
|
|
23
|
+
* an accepted push that evaporates on restart would silently break refs.
|
|
24
|
+
*
|
|
25
|
+
* Hydration fails closed: a corrupt refs or review file throws at
|
|
26
|
+
* construction instead of starting empty, because an empty start would
|
|
27
|
+
* drop protected-ref policies and recorded approvals — the exact gates
|
|
28
|
+
* this store exists to persist.
|
|
29
|
+
*/
|
|
30
|
+
import * as crypto from 'node:crypto';
|
|
31
|
+
import * as fs from 'node:fs';
|
|
32
|
+
import * as path from 'node:path';
|
|
33
|
+
import { assertPushableLayer, assertReportDigest, LayerPushError, } from './layer-registry.js';
|
|
34
|
+
/** `blake3:<hex>` — the only id shape `computeLayerId` emits. Anything else
|
|
35
|
+
* is rejected before it can become a filename (no traversal). */
|
|
36
|
+
const LAYER_ID_REGEX = /^blake3:([0-9a-f]{64})$/;
|
|
37
|
+
/** Review ids are route-minted UUIDs; permit that shape only. */
|
|
38
|
+
const REVIEW_ID_REGEX = /^[0-9a-fA-F-]{1,64}$/;
|
|
39
|
+
export class FsLayerRegistry {
|
|
40
|
+
layersDir;
|
|
41
|
+
reviewsDir;
|
|
42
|
+
reportsDir;
|
|
43
|
+
refsFile;
|
|
44
|
+
refs = new Map();
|
|
45
|
+
reviews = new Map();
|
|
46
|
+
layerCount;
|
|
47
|
+
reportCount;
|
|
48
|
+
maxLayers;
|
|
49
|
+
maxRefs;
|
|
50
|
+
maxReviews;
|
|
51
|
+
maxReports;
|
|
52
|
+
constructor(dataDir, limits = {}) {
|
|
53
|
+
this.maxLayers = limits.maxLayers ?? 10_000;
|
|
54
|
+
this.maxRefs = limits.maxRefs ?? 1_000;
|
|
55
|
+
this.maxReviews = limits.maxReviews ?? 10_000;
|
|
56
|
+
this.maxReports = limits.maxReports ?? 10_000;
|
|
57
|
+
const root = path.join(dataDir, 'layer-registry');
|
|
58
|
+
this.layersDir = path.join(root, 'layers');
|
|
59
|
+
this.reviewsDir = path.join(root, 'reviews');
|
|
60
|
+
this.reportsDir = path.join(root, 'reports');
|
|
61
|
+
this.refsFile = path.join(root, 'refs.json');
|
|
62
|
+
fs.mkdirSync(this.layersDir, { recursive: true });
|
|
63
|
+
fs.mkdirSync(this.reviewsDir, { recursive: true });
|
|
64
|
+
fs.mkdirSync(this.reportsDir, { recursive: true });
|
|
65
|
+
this.layerCount = this.listLayerFiles().length;
|
|
66
|
+
this.reportCount = fs.readdirSync(this.reportsDir).filter((n) => /^[0-9a-f]{64}$/.test(n)).length;
|
|
67
|
+
this.hydrateRefs();
|
|
68
|
+
this.hydrateReviews();
|
|
69
|
+
}
|
|
70
|
+
hydrateRefs() {
|
|
71
|
+
let raw;
|
|
72
|
+
try {
|
|
73
|
+
raw = fs.readFileSync(this.refsFile, 'utf8');
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
if (err.code === 'ENOENT')
|
|
77
|
+
return; // fresh registry
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
const parsed = JSON.parse(raw);
|
|
81
|
+
for (const [name, entry] of Object.entries(parsed))
|
|
82
|
+
this.refs.set(name, entry);
|
|
83
|
+
}
|
|
84
|
+
hydrateReviews() {
|
|
85
|
+
for (const name of fs.readdirSync(this.reviewsDir)) {
|
|
86
|
+
if (!name.endsWith('.json'))
|
|
87
|
+
continue; // skip in-flight temp files
|
|
88
|
+
const review = JSON.parse(fs.readFileSync(path.join(this.reviewsDir, name), 'utf8'));
|
|
89
|
+
this.reviews.set(review.id, review);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
listLayerFiles() {
|
|
93
|
+
return fs.readdirSync(this.layersDir).filter((n) => /^[0-9a-f]{64}\.json$/.test(n));
|
|
94
|
+
}
|
|
95
|
+
layerFile(layerId) {
|
|
96
|
+
const m = LAYER_ID_REGEX.exec(layerId);
|
|
97
|
+
return m ? path.join(this.layersDir, `${m[1]}.json`) : undefined;
|
|
98
|
+
}
|
|
99
|
+
writeAtomic(target, data) {
|
|
100
|
+
const tmp = `${target}.tmp-${crypto.randomUUID()}`;
|
|
101
|
+
// fsync before rename: "durable before ack" must hold through power
|
|
102
|
+
// loss, not just process crash — rename alone leaves the data in the
|
|
103
|
+
// page cache.
|
|
104
|
+
const fd = fs.openSync(tmp, 'w');
|
|
105
|
+
try {
|
|
106
|
+
// writeSync's overloads take a buffer OR a string, never the union —
|
|
107
|
+
// normalize to a buffer before the call.
|
|
108
|
+
fs.writeSync(fd, typeof data === 'string' ? Buffer.from(data, 'utf-8') : data);
|
|
109
|
+
fs.fsyncSync(fd);
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
fs.closeSync(fd);
|
|
113
|
+
}
|
|
114
|
+
fs.renameSync(tmp, target);
|
|
115
|
+
}
|
|
116
|
+
push(file) {
|
|
117
|
+
const id = assertPushableLayer(file);
|
|
118
|
+
const target = this.layerFile(id);
|
|
119
|
+
if (!target)
|
|
120
|
+
throw new LayerPushError('id-mismatch', `unsupported content-address shape ${id}`);
|
|
121
|
+
const bytes = JSON.stringify(file);
|
|
122
|
+
// First write wins (same rationale as the memory store): a re-push of an
|
|
123
|
+
// existing id must be byte-identical, or an attacker could swap the
|
|
124
|
+
// non-canonical parts (signatures, derived content) under a trusted id.
|
|
125
|
+
let existing;
|
|
126
|
+
try {
|
|
127
|
+
existing = fs.readFileSync(target, 'utf8');
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
if (err.code !== 'ENOENT')
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
if (existing !== undefined) {
|
|
134
|
+
if (existing !== bytes) {
|
|
135
|
+
throw new LayerPushError('content-conflict', `layer ${id} already exists with different non-canonical bytes; refusing overwrite`);
|
|
136
|
+
}
|
|
137
|
+
return id;
|
|
138
|
+
}
|
|
139
|
+
if (this.layerCount >= this.maxLayers) {
|
|
140
|
+
throw new LayerPushError('registry-full', `registry holds ${this.layerCount} layers (cap ${this.maxLayers})`);
|
|
141
|
+
}
|
|
142
|
+
this.writeAtomic(target, bytes);
|
|
143
|
+
this.layerCount += 1;
|
|
144
|
+
return id;
|
|
145
|
+
}
|
|
146
|
+
hasLayer(layerId) {
|
|
147
|
+
const file = this.layerFile(layerId);
|
|
148
|
+
return file !== undefined && fs.existsSync(file);
|
|
149
|
+
}
|
|
150
|
+
listLayers() {
|
|
151
|
+
return this.listLayerFiles().map((n) => `blake3:${n.slice(0, -'.json'.length)}`);
|
|
152
|
+
}
|
|
153
|
+
// LayerRefStore — consumed by the shared merge flow. The disk round-trip
|
|
154
|
+
// gives callers an isolated copy for free (no shared references).
|
|
155
|
+
loadLayer(layerId) {
|
|
156
|
+
const file = this.layerFile(layerId);
|
|
157
|
+
if (file !== undefined) {
|
|
158
|
+
try {
|
|
159
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
if (err.code !== 'ENOENT')
|
|
163
|
+
throw err;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
throw new Error(`No layer ${layerId} in registry`);
|
|
167
|
+
}
|
|
168
|
+
storeLayer(file) {
|
|
169
|
+
// Merge layers arrive from the shared flow already content-addressed;
|
|
170
|
+
// run them through the same integrity gate as external pushes.
|
|
171
|
+
return this.push(file);
|
|
172
|
+
}
|
|
173
|
+
getRef(name) {
|
|
174
|
+
const entry = this.refs.get(name);
|
|
175
|
+
return entry === undefined ? undefined : structuredClone(entry);
|
|
176
|
+
}
|
|
177
|
+
setRef(name, entry) {
|
|
178
|
+
if (!this.refs.has(name) && this.refs.size >= this.maxRefs) {
|
|
179
|
+
throw new LayerPushError('registry-full', `registry holds ${this.refs.size} refs (cap ${this.maxRefs})`);
|
|
180
|
+
}
|
|
181
|
+
const next = structuredClone({
|
|
182
|
+
layers: entry.layers,
|
|
183
|
+
...(entry.policy ? { policy: entry.policy } : {}),
|
|
184
|
+
});
|
|
185
|
+
// Persist before committing to memory: if the write throws, the served
|
|
186
|
+
// state still matches disk.
|
|
187
|
+
const record = Object.fromEntries([...this.refs.entries(), [name, next]]);
|
|
188
|
+
this.writeAtomic(this.refsFile, JSON.stringify(record));
|
|
189
|
+
this.refs.set(name, next);
|
|
190
|
+
}
|
|
191
|
+
listRefs() {
|
|
192
|
+
return Object.fromEntries([...this.refs.entries()].map(([name, entry]) => [name, structuredClone(entry)]));
|
|
193
|
+
}
|
|
194
|
+
getReview(id) {
|
|
195
|
+
const review = this.reviews.get(id);
|
|
196
|
+
return review === undefined ? undefined : structuredClone(review);
|
|
197
|
+
}
|
|
198
|
+
listReviews() {
|
|
199
|
+
return [...this.reviews.values()].map((review) => structuredClone(review));
|
|
200
|
+
}
|
|
201
|
+
putReview(review) {
|
|
202
|
+
if (!REVIEW_ID_REGEX.test(review.id)) {
|
|
203
|
+
throw new Error(`review id ${JSON.stringify(review.id)} is not filename-safe`);
|
|
204
|
+
}
|
|
205
|
+
if (!this.reviews.has(review.id) && this.reviews.size >= this.maxReviews) {
|
|
206
|
+
throw new LayerPushError('registry-full', `registry holds ${this.reviews.size} reviews (cap ${this.maxReviews})`);
|
|
207
|
+
}
|
|
208
|
+
const next = structuredClone(review);
|
|
209
|
+
this.writeAtomic(path.join(this.reviewsDir, `${review.id}.json`), JSON.stringify(next));
|
|
210
|
+
this.reviews.set(review.id, next);
|
|
211
|
+
}
|
|
212
|
+
putReport(digest, bytes) {
|
|
213
|
+
assertReportDigest(digest, bytes); // also pins the blake3:<hex> shape (no traversal)
|
|
214
|
+
const target = path.join(this.reportsDir, digest.slice('blake3:'.length));
|
|
215
|
+
// Content-addressed and digest-verified, so a re-put is idempotent by
|
|
216
|
+
// construction; keep the first write.
|
|
217
|
+
if (fs.existsSync(target))
|
|
218
|
+
return digest;
|
|
219
|
+
if (this.reportCount >= this.maxReports) {
|
|
220
|
+
throw new LayerPushError('registry-full', `registry holds ${this.reportCount} reports (cap ${this.maxReports})`);
|
|
221
|
+
}
|
|
222
|
+
this.writeAtomic(target, bytes);
|
|
223
|
+
this.reportCount += 1;
|
|
224
|
+
return digest;
|
|
225
|
+
}
|
|
226
|
+
getReport(digest) {
|
|
227
|
+
const m = LAYER_ID_REGEX.exec(digest);
|
|
228
|
+
if (!m)
|
|
229
|
+
return undefined;
|
|
230
|
+
try {
|
|
231
|
+
return new Uint8Array(fs.readFileSync(path.join(this.reportsDir, m[1])));
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
if (err.code === 'ENOENT')
|
|
235
|
+
return undefined;
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=layer-registry-fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-registry-fs.js","sourceRoot":"","sources":["../src/layer-registry-fs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP layer-registry route (10-registry.md): push/pull content-addressed
|
|
3
|
+
* layers by id, a ref database with server-side policy enforcement, and
|
|
4
|
+
* review (PR) objects.
|
|
5
|
+
*
|
|
6
|
+
* POST /api/v1/layers push (id verified server-side)
|
|
7
|
+
* GET /api/v1/layers list ids
|
|
8
|
+
* GET /api/v1/layers/:id pull
|
|
9
|
+
* GET /api/v1/refs list refs + policies
|
|
10
|
+
* PUT /api/v1/refs/:name create / move / protect
|
|
11
|
+
* POST /api/v1/refs/:name/merge shared merge flow, policies enforced
|
|
12
|
+
* POST /api/v1/reviews open a review (PR object)
|
|
13
|
+
* GET /api/v1/reviews[/:id] read review(s)
|
|
14
|
+
* POST /api/v1/reviews/:id/feedback per-entity decisions + status
|
|
15
|
+
*
|
|
16
|
+
* Policy-bearing refs cannot be moved by PUT — only the merge endpoint
|
|
17
|
+
* moves them, which is where required checks and approval rules run.
|
|
18
|
+
* Authentication mirrors the blob route: the websocket `authenticate`
|
|
19
|
+
* hook is adapted into an authorizer, so one token scheme covers sync,
|
|
20
|
+
* blobs, and the registry.
|
|
21
|
+
*
|
|
22
|
+
* v1 visibility: any authenticated principal reads ALL layers and refs
|
|
23
|
+
* (team-scoped registry); per-ref/per-layer visibility is the
|
|
24
|
+
* public/internal/private roadmap work (10 §10.5). Approval is a
|
|
25
|
+
* point-in-time check — the lookup and the merge run synchronously in
|
|
26
|
+
* one request, but an approval withdrawn after a merge completed does
|
|
27
|
+
* not un-merge it.
|
|
28
|
+
*/
|
|
29
|
+
import * as http from 'node:http';
|
|
30
|
+
import { type Principal } from './auth.js';
|
|
31
|
+
import { type LayerRegistryStore } from './layer-registry.js';
|
|
32
|
+
import { type RegistryWebhook } from './registry-webhooks.js';
|
|
33
|
+
/** Resolve the acting principal, or null to reject with 401. */
|
|
34
|
+
export type RegistryAuthorizeFn = (token: string | undefined, method: string) => Promise<Principal | null> | Principal | null;
|
|
35
|
+
export interface LayerRegistryRouteOptions {
|
|
36
|
+
registry: LayerRegistryStore;
|
|
37
|
+
/** Reject layer pushes over this size (default 50 MB). */
|
|
38
|
+
maxBytes?: number;
|
|
39
|
+
/** When omitted, traffic is anonymous (dev/tests) — matches the blob route. */
|
|
40
|
+
authorize?: RegistryAuthorizeFn;
|
|
41
|
+
/** Event consumers (08-review.md §8.7); empty = no emission. */
|
|
42
|
+
webhooks?: readonly RegistryWebhook[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Handle a registry request. Returns false (untouched response) when the
|
|
46
|
+
* path is not a registry path, true when a response was written.
|
|
47
|
+
*/
|
|
48
|
+
export declare function handleLayerRegistryRequest(req: http.IncomingMessage, res: http.ServerResponse, opts: LayerRegistryRouteOptions): Promise<boolean>;
|
|
49
|
+
//# sourceMappingURL=layer-registry-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-registry-route.d.ts","sourceRoot":"","sources":["../src/layer-registry-route.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAMlC,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAEL,KAAK,kBAAkB,EAIxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEjF,gEAAgE;AAChE,MAAM,MAAM,mBAAmB,GAAG,CAChC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,KACX,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC;AAElD,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;CACvC;AAuKD;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,EACxB,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,OAAO,CAAC,CA+gBlB"}
|