From 329345302d09553503ff88fd1e9d271587445a58 Mon Sep 17 00:00:00 2001 From: Archita Singh Date: Sat, 11 Jul 2026 02:02:27 +0530 Subject: [PATCH] feat: implement custom context separation profiles (#36) --- src/server.mjs | 25 +++++++++++++++++-------- src/service.mjs | 5 +++-- src/store.mjs | 16 +++++++++++----- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/server.mjs b/src/server.mjs index 655fd5a..be448d7 100644 --- a/src/server.mjs +++ b/src/server.mjs @@ -147,28 +147,27 @@ async function route(service, request, url, body) { // App/Agent propose contribution route if (request.method === "POST" && path === "/v1/contributions/propose") { return service.proposeContribution(readMemactApiKey(request), body, { - connectionId: request.headers["x-memact-connection-id"] + connectionId: request.headers["x-memact-connection-id"], + database_partition: readDatabasePartition(request, url) }) } // App/Agent Context Rectification Protocol (CRP) route if (request.method === "POST" && path === "/v1/contributions/rectify") { return service.rectifyContribution(readMemactApiKey(request), body, { - connectionId: request.headers["x-memact-connection-id"] + connectionId: request.headers["x-memact-connection-id"], + database_partition: readDatabasePartition(request, url) }) } // Scoped Context Packet (CAP) request route if (request.method === "POST" && path === "/v1/cap/request") { return service.requestContextPacket(readMemactApiKey(request), body, { - connectionId: request.headers["x-memact-connection-id"] + connectionId: request.headers["x-memact-connection-id"], + database_partition: readDatabasePartition(request, url) }) } - if (request.method === "GET" && path === "/v1/credits") { - return service.listCredits(readMemactApiKey(request)) - } - // User authenticated session const auth = await service.authenticateSession(request.headers.authorization) if (request.method === "GET" && path === "/v1/me") { @@ -254,6 +253,16 @@ function isSubContextDefinitionPath(path) { && (path.endsWith("/subcontexts") || path.endsWith("/subschemas")) } +function readDatabasePartition(request, url) { + const headerPartition = request.headers["x-database-partition"]; + if (headerPartition) return headerPartition; + + const queryPartition = url?.searchParams?.get("database_partition"); + if (queryPartition) return queryPartition; + + return "db_work"; +} + function parseList(value) { return String(value || "") .split(",") @@ -388,7 +397,7 @@ function send(response, status, payload, request) { response.setHeader("Access-Control-Allow-Origin", origin) response.setHeader("Vary", "Origin") } - response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Memact-API-Key, X-Memact-Connection-Id") + response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Memact-API-Key, X-Memact-Connection-Id,X-Database-Partition") response.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS") response.setHeader("X-Content-Type-Options", "nosniff") response.setHeader("Referrer-Policy", "no-referrer") diff --git a/src/service.mjs b/src/service.mjs index cdc51a6..648344b 100644 --- a/src/service.mjs +++ b/src/service.mjs @@ -22,8 +22,6 @@ import { unknownCategories, unknownScopes } from "./policy.mjs" -import { buildCapPacket } from "../../Memory/src/cap-packet.mjs" - @@ -505,6 +503,7 @@ export class AccessService { } const connectionId = body.connection_id || options.connectionId || "" const access = await this.verifyApiAccess(apiKey, ["capture:event_write"], [category], connectionId) + const database_partition = options.database_partition || body.database_partition || "db_work" return this.mutate(async (data) => { const event = { id: randomId("evt"), @@ -544,6 +543,7 @@ export class AccessService { const data = await this.store.read() assertUser(data, userId) const ownedAppIds = new Set(data.apps.filter((app) => app.owner_user_id === userId).map((app) => app.id)) + const targetPartition = filter.database_partition || "db_work" return { events: data.capture_events .filter((event) => ownedAppIds.has(event.app_id)) @@ -723,6 +723,7 @@ export class AccessService { const access = await this.verifyApiAccess(apiKey, ["context:write"], [category], connectionId) const now = timestamp(this.now()) const sourceTrail = normalizeSourceTrail(proposalInput) + const database_partition = options.database_partition || body.database_partition || "db_work" const contextValue = proposalInput.kind === "raw_signal" ? contextFromRawSignal(proposalInput) : sanitizeProposalContext(proposalInput.context || proposalInput.value || {}) diff --git a/src/store.mjs b/src/store.mjs index 5391d84..89d18b9 100644 --- a/src/store.mjs +++ b/src/store.mjs @@ -95,11 +95,17 @@ export function migrateStore(data) { audit_log: Array.isArray(base.audit_log) ? base.audit_log : [], usage_events: Array.isArray(base.usage_events) ? base.usage_events : [], wiki_proposals: Array.isArray(base.wiki_proposals) ? base.wiki_proposals : [], - capture_events: Array.isArray(base.capture_events) ? base.capture_events : [], - schema_definitions: Array.isArray(base.schema_definitions) ? base.schema_definitions : [], - subschema_definitions: Array.isArray(base.subschema_definitions) ? base.subschema_definitions : [], - schema_packets: Array.isArray(base.schema_packets) ? base.schema_packets : [], - memory_records: Array.isArray(base.memory_records) ? base.memory_records : [], + + capture_events: Array.isArray(base.capture_events) + ? base.capture_events.map(ev => ({ ...ev, database_partition: ev.database_partition || "db_work" })) + : [], + schema_packets: Array.isArray(base.schema_packets) + ? base.schema_packets.map(p => ({ ...p, database_partition: p.database_partition || "db_work" })) + : [], + memory_records: Array.isArray(base.memory_records) + ? base.memory_records.map(m => ({ ...m, database_partition: m.database_partition || "db_work" })) + : [], + feature_connections: Array.isArray(base.feature_connections) ? base.feature_connections : [], feature_registry: Array.isArray(base.feature_registry) ? base.feature_registry : [], feature_runs: Array.isArray(base.feature_runs) ? base.feature_runs : []