From ffe6286c92ff3e4668c388e1e2184ed5d8f9d5ee Mon Sep 17 00:00:00 2001 From: Pengfei Date: Sat, 11 Apr 2026 11:27:06 +0800 Subject: [PATCH] feat(cloudflare): support explicit APP_PID for shared-identity deployments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Media Kit is deployed alongside sibling CF Workers that share one Blocklet Server identity (e.g. aigne-hub + payment-kit + media-kit all mounted under one logical instance), the current `instanceDid: 'auto'` path in ensureRegistered() causes two problems: 1. The derived DID (from APP_SK via fromSecretKey) does not match the intended permanent DID (derived from APP_PSK). blocklet-service's registerApp() then finds a stale row via findInstancesByAppSk() and triggers migrateInstanceDid(), moving all settings/memberships/ security_rules/audit_logs from one instance_did to another. 2. Media Kit always writes appName="Media Kit" into the shared instance's app:name row, overwriting whatever branding the owning component (e.g. aigne-hub) set. In a multi-minute race with the other component's own registerApp() call, app:name oscillates with each worker's schedule, and user-facing login pages flicker. Fix: treat env.APP_PID as an opt-in "shared identity mode" flag. - When APP_PID is set: pass it as explicit instanceDid, pass APP_PSK (new optional secret) for delegation, and skip appName / appDescription so we never overwrite the owner's branding. - When APP_PID is unset: fall back to the old 'auto' behaviour with Media Kit's own appName (unchanged for single-tenant deploys). Deployment notes: - Set APP_PID as a plain var in wrangler.toml/[vars] (public DID). - Set APP_PSK as a secret via `wrangler secret put APP_PSK` — this is the 64-byte ED25519 expanded permanent signer key; leave unset for single-tenant deployments. Verified against a staging deployment of aigne-hub + payment-kit + media-kit sharing APP_PID=zNKWm5HBg...; after this patch, blocklet-service settings stay stable under the shared DID and app:name="AIGNE Hub (Staging)" no longer flips on media-kit boot. --- cloudflare/src/types.ts | 7 +++++++ cloudflare/src/worker.ts | 25 ++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/cloudflare/src/types.ts b/cloudflare/src/types.ts index 088ef3d..df64bae 100644 --- a/cloudflare/src/types.ts +++ b/cloudflare/src/types.ts @@ -31,6 +31,13 @@ export interface Env { APP_SK: string; // 64-byte hex secret key — used to register & derive instance DID APP_NAME: string; APP_PID: string; // Derived from APP_SK after registerApp; can also be set explicitly + /** + * Optional permanent signer key (ED25519 expanded form) for PSK delegation. + * When APP_PID is set explicitly and refers to a DID that differs from + * fromSecretKey(APP_SK).address, pass APP_PSK so blocklet-service stores + * the delegation pair and buildIdentity() returns the correct (appDid, appPid). + */ + APP_PSK?: string; APP_PREFIX: string; // Mount prefix (e.g. '/media-kit') — empty or '/' means root // Auth Service (DID Connect via Service Binding) AUTH_SERVICE: { diff --git a/cloudflare/src/worker.ts b/cloudflare/src/worker.ts index bac89fc..be338bb 100644 --- a/cloudflare/src/worker.ts +++ b/cloudflare/src/worker.ts @@ -99,11 +99,30 @@ async function ensureRegistered(env: Env): Promise { return env.APP_PID || ''; } try { + // When APP_PID is set explicitly we're in "shared identity" mode: this + // worker is part of a multi-component deployment (e.g. aigne-hub + + // payment-kit + media-kit all sharing one Blocklet Server identity) and + // another component owns the user-facing instance branding. + // - Pass the explicit instanceDid so blocklet-service does NOT derive + // a fresh DID from APP_SK and trigger a destructive migrateInstanceDid + // against the sibling component. + // - Pass APP_PSK (if set) so buildIdentity can return the correct + // delegated (appDid, appPid) pair. + // - Do NOT pass appName/appDescription so we don't overwrite the + // owner component's branding every time media-kit boots. + // When APP_PID is unset (legacy single-tenant deploy) fall back to the + // old 'auto' behaviour with our own branding. + const isSharedIdentity = !!env.APP_PID; const result = await env.AUTH_SERVICE.registerApp({ - instanceDid: 'auto', + instanceDid: env.APP_PID || 'auto', appSk: env.APP_SK, - appName: env.APP_NAME || 'Media Kit', - appDescription: 'Media asset management', + ...(env.APP_PSK ? { appPsk: env.APP_PSK } : {}), + ...(isSharedIdentity + ? {} + : { + appName: env.APP_NAME || 'Media Kit', + appDescription: 'Media asset management', + }), }); registeredInstanceDid = result.instanceDid; console.log(`[media-kit] Registered as instance: ${registeredInstanceDid}`);