@codemem/server 0.35.2 → 0.36.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/dist/index.js +21 -48
- package/dist/index.js.map +1 -1
- package/dist/routes/memory.d.ts.map +1 -1
- package/dist/routes/sync.d.ts.map +1 -1
- package/package.json +7 -7
- package/static/app.js +566 -447
- package/static/index.html +1 -1
package/dist/index.js
CHANGED
|
@@ -556,37 +556,14 @@ function queryBool(value) {
|
|
|
556
556
|
}
|
|
557
557
|
//#endregion
|
|
558
558
|
//#region src/routes/memory.ts
|
|
559
|
-
function
|
|
560
|
-
if (typeof value !== "string") return null;
|
|
561
|
-
const trimmed = value.trim();
|
|
562
|
-
return trimmed ? trimmed : null;
|
|
563
|
-
}
|
|
564
|
-
function buildOwnershipContext(store) {
|
|
565
|
-
const claimedPeerIds = new Set(store.sameActorPeerIds());
|
|
566
|
-
return {
|
|
567
|
-
actorId: store.actorId,
|
|
568
|
-
claimedPeerIds,
|
|
569
|
-
deviceId: store.deviceId,
|
|
570
|
-
legacyActorIds: new Set(Array.from(claimedPeerIds, (peerId) => `legacy-sync:${peerId}`))
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
function memoryOwnedBySelf(row, ownership, metadata = fromJson(row.metadata_json ?? null)) {
|
|
574
|
-
const meta = metadata;
|
|
575
|
-
const actorId = cleanOwnershipValue(row.actor_id) ?? cleanOwnershipValue(meta.actor_id);
|
|
576
|
-
if (actorId === ownership.actorId) return true;
|
|
577
|
-
const deviceId = cleanOwnershipValue(row.origin_device_id) ?? cleanOwnershipValue(meta.origin_device_id);
|
|
578
|
-
if (deviceId === ownership.deviceId) return true;
|
|
579
|
-
if (deviceId && ownership.claimedPeerIds.has(deviceId)) return true;
|
|
580
|
-
if (actorId && ownership.legacyActorIds.has(actorId)) return true;
|
|
581
|
-
return false;
|
|
582
|
-
}
|
|
583
|
-
function serializeMemoryRow(ownership, row) {
|
|
559
|
+
function serializeMemoryRow(ownedBySelf, row) {
|
|
584
560
|
const metadata = fromJson(row.metadata_json ?? null);
|
|
561
|
+
const owned_by_self = ownedBySelf(row);
|
|
585
562
|
return {
|
|
586
563
|
...row,
|
|
587
564
|
kind: canonicalMemoryKind(row.kind ?? null, row.metadata_json),
|
|
588
565
|
metadata_json: metadata,
|
|
589
|
-
owned_by_self
|
|
566
|
+
owned_by_self
|
|
590
567
|
};
|
|
591
568
|
}
|
|
592
569
|
/**
|
|
@@ -642,11 +619,7 @@ function normalizeScope(raw) {
|
|
|
642
619
|
if (value === "mine" || value === "theirs") return value;
|
|
643
620
|
}
|
|
644
621
|
function buildViewerMemoryFilters(store, filters) {
|
|
645
|
-
return buildFilterClausesWithContext(filters,
|
|
646
|
-
actorId: store.actorId,
|
|
647
|
-
deviceId: store.deviceId,
|
|
648
|
-
enforceScopeVisibility: true
|
|
649
|
-
});
|
|
622
|
+
return buildFilterClausesWithContext(filters, store.ownershipFilterContext());
|
|
650
623
|
}
|
|
651
624
|
function countVisibleMemoryRows(store, filters) {
|
|
652
625
|
const filterResult = buildViewerMemoryFilters(store, filters);
|
|
@@ -729,8 +702,8 @@ function queryMemoryPage(store, options) {
|
|
|
729
702
|
WHERE ${where}
|
|
730
703
|
ORDER BY memory_items.created_at DESC
|
|
731
704
|
LIMIT ? OFFSET ?`).all(...filterResult.params, options.limit + 1, options.offset);
|
|
732
|
-
const
|
|
733
|
-
return rows.map((row) => serializeMemoryRow(
|
|
705
|
+
const ownedBySelf = store.buildOwnershipPredicate();
|
|
706
|
+
return rows.map((row) => serializeMemoryRow(ownedBySelf, row));
|
|
734
707
|
}
|
|
735
708
|
function isSummaryLikeMemory$1(item) {
|
|
736
709
|
return isSummaryLikeMemory({
|
|
@@ -1010,7 +983,7 @@ function memoryRoutes(getStore) {
|
|
|
1010
983
|
if (!store.get(memoryId)) return c.json({ error: "memory not found" }, 404);
|
|
1011
984
|
const row = drizzle(store.db, { schema }).select().from(schema.memoryItems).where(eq(schema.memoryItems.id, memoryId)).get();
|
|
1012
985
|
if (!row) return c.json({ error: "memory not found" }, 404);
|
|
1013
|
-
if (!memoryOwnedBySelf(row
|
|
986
|
+
if (!store.memoryOwnedBySelf(row)) return c.json({ error: "memory not owned by this device" }, 403);
|
|
1014
987
|
if (Number(row.active ?? 1) === 0 || row.deleted_at != null) return c.json({ status: "ok" });
|
|
1015
988
|
try {
|
|
1016
989
|
store.forget(memoryId);
|
|
@@ -1858,13 +1831,12 @@ async function maybeGrantDefaultSpaceOnJoin(opts) {
|
|
|
1858
1831
|
const scopeId = preferences.default_space_scope_id?.trim();
|
|
1859
1832
|
if (!scopeId) return null;
|
|
1860
1833
|
if (scopeId !== defaultSpaceScopeIdForGroup(opts.groupId)) return null;
|
|
1861
|
-
|
|
1834
|
+
if (((await coordinatorListScopesAction({
|
|
1862
1835
|
groupId: opts.groupId,
|
|
1863
1836
|
includeInactive: false,
|
|
1864
1837
|
remoteUrl: opts.config.syncCoordinatorUrl || null,
|
|
1865
1838
|
adminSecret: opts.config.syncCoordinatorAdminSecret || null
|
|
1866
|
-
})).find((scope) => scope.scope_id === scopeId) ?? null;
|
|
1867
|
-
if (!defaultScope || defaultScope.kind !== "team_default") return null;
|
|
1839
|
+
})).find((scope) => scope.scope_id === scopeId) ?? null)?.kind !== "team_default") return null;
|
|
1868
1840
|
return await coordinatorGrantScopeMembershipAction({
|
|
1869
1841
|
groupId: opts.groupId,
|
|
1870
1842
|
scopeId,
|
|
@@ -3107,9 +3079,14 @@ var PEERS_QUERY = `
|
|
|
3107
3079
|
* honor scoped-sync wire features such as explicit `scope_id` parameters
|
|
3108
3080
|
* and `/v1/status` `authorized_scopes` enumeration.
|
|
3109
3081
|
*
|
|
3110
|
-
*
|
|
3111
|
-
*
|
|
3112
|
-
*
|
|
3082
|
+
* Threat model: this header is intentionally only a best-effort capability
|
|
3083
|
+
* advertisement. It is not signed, so an authenticated paired peer — or a
|
|
3084
|
+
* bootstrap worker presenting a valid grant on `/v1/status` — can claim
|
|
3085
|
+
* `scoped` support and cause status to enumerate the scopes that caller is
|
|
3086
|
+
* already authorized to see. That is metadata disclosure inside the paired-peer
|
|
3087
|
+
* or bootstrap-grant trust boundary, not data authorization. Every scoped data
|
|
3088
|
+
* path still requires signed requests and checks membership before returning
|
|
3089
|
+
* scope contents.
|
|
3113
3090
|
*/
|
|
3114
3091
|
function negotiatedSyncCapability(c) {
|
|
3115
3092
|
return negotiateSyncCapability(LOCAL_SYNC_CAPABILITY, normalizeSyncCapability(c.req.header(SYNC_CAPABILITY_HEADER) ?? null));
|
|
@@ -3157,10 +3134,8 @@ function syncProtocolRoutes(getStore, opts = {}) {
|
|
|
3157
3134
|
}
|
|
3158
3135
|
if (isSyncAuthStoreBusy(auth)) return syncAuthStoreBusyResponse(c);
|
|
3159
3136
|
if (!auth.ok) {
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
console.warn(`[sync] bootstrap grant auth failed: reason=${auth.reason} grant_present=${grantPresent} path=${c.req.path}`);
|
|
3163
|
-
}
|
|
3137
|
+
const grantPresent = Boolean((c.req.header("X-Codemem-Bootstrap-Grant") ?? "").trim());
|
|
3138
|
+
if (bootstrapAttempted && grantPresent) console.warn(`[sync] bootstrap grant auth failed: reason=${auth.reason} grant_present=${grantPresent} path=${c.req.path}`);
|
|
3164
3139
|
const wireReason = bootstrapAttempted ? "bootstrap_grant_invalid" : auth.reason;
|
|
3165
3140
|
return (preauthChecked ? null : rateLimitedResponse(c, c.req.path, false)) ?? c.json(unauthorizedPayload(wireReason, true), 401);
|
|
3166
3141
|
}
|
|
@@ -3268,10 +3243,8 @@ function syncProtocolRoutes(getStore, opts = {}) {
|
|
|
3268
3243
|
}
|
|
3269
3244
|
if (isSyncAuthStoreBusy(auth)) return syncAuthStoreBusyResponse(c);
|
|
3270
3245
|
if (!auth.ok) {
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
console.warn(`[sync] bootstrap grant auth failed: reason=${auth.reason} grant_present=${grantPresent} path=${c.req.path}`);
|
|
3274
|
-
}
|
|
3246
|
+
const grantPresent = Boolean((c.req.header("X-Codemem-Bootstrap-Grant") ?? "").trim());
|
|
3247
|
+
if (bootstrapAttempted && grantPresent) console.warn(`[sync] bootstrap grant auth failed: reason=${auth.reason} grant_present=${grantPresent} path=${c.req.path}`);
|
|
3275
3248
|
const wireReason = bootstrapAttempted ? "bootstrap_grant_invalid" : auth.reason;
|
|
3276
3249
|
return (preauthChecked ? null : rateLimitedResponse(c, c.req.path, false)) ?? c.json(unauthorizedPayload(wireReason, true), 401);
|
|
3277
3250
|
}
|