affaan-m/ECC · error · Error

No trusted boundary policy is configured for memory scope "$

Error message

No trusted boundary policy is configured for memory scope "${scope}".

What it means

Thrown by assertMemoryRootSafe() when the hidden Symbol-keyed boundary map (roots[VAULT_ROOT_BOUNDARIES]) does not contain a non-empty string for the requested scope. The boundary is the trusted ancestor directory that the root path must stay inside (defense against symlink escapes). resolveVaultRoots() installs this Symbol property non-enumerably; a hand-built or stripped roots object will not have it.

Source

Thrown at scripts/lib/memory-vault.js:107

    }),
    enumerable: false,
    configurable: false,
    writable: false,
  });
  return Object.freeze(roots);
}

function assertMemoryRootSafe(roots, scope) {
  if (!roots || typeof roots !== 'object' || Array.isArray(roots)) {
    throw new Error('Memory roots must include a trusted boundary policy.');
  }
  const root = roots[scope];
  if (typeof root !== 'string' || root.length === 0) {
    throw new Error(`No memory root is configured for scope "${scope}".`);
  }
  const boundary = roots[VAULT_ROOT_BOUNDARIES]?.[scope];
  if (typeof boundary !== 'string' || boundary.length === 0) {
    throw new Error(`No trusted boundary policy is configured for memory scope "${scope}".`);
  }
  assertWithinTrustedRoot(root, boundary, 'access memory through a symlink');
  if (fs.existsSync(root) && fs.lstatSync(root).isSymbolicLink()) {
    throw new Error(`Refusing to access memory through symlink root: ${root}`);
  }
  return root;
}

function assertMemoryDirectorySafe(directory, root) {
  assertWithinTrustedRoot(directory, root, 'access memory directory');
  if (fs.existsSync(directory) && fs.lstatSync(directory).isSymbolicLink()) {
    throw new Error(`Refusing to access memory through symlink directory: ${directory}`);
  }
  return directory;
}

function sameFileIdentity(left, right) {
  // The inode is the primary identity signal and must always match.

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Always use resolveVaultRoots() to produce roots — it installs the Symbol boundary map correctly.
  2. Never clone roots via spread, Object.assign, or JSON round-trip; pass the original frozen object.
  3. If you must override a path, call resolveVaultRoots({ ECC_MEMORY_PROJECT_ROOT, ECC_MEMORY_USER_ROOT }) again.
  4. Treat the boundary map as load-bearing security state — do not strip non-enumerable keys.

Example fix

// before — drops the Symbol boundary map
const roots = { ...resolveVaultRoots(), project: '/override' };
saveMemory(input, { roots });

// after — re-resolve with env override so the boundary map survives
const roots = resolveVaultRoots({ ECC_MEMORY_PROJECT_ROOT: '/override' });
saveMemory(input, { roots });
Defensive patterns

Strategy: validation

Validate before calling

// roots must come from resolveVaultRoots(); never spread or JSON-round-trip
if (!options.roots) {
  options.roots = resolveVaultRoots({
    ECC_MEMORY_PROJECT_ROOT: process.env.ECC_MEMORY_PROJECT_ROOT,
    ECC_MEMORY_USER_ROOT: process.env.ECC_MEMORY_USER_ROOT,
  });
}

Prevention

When it happens

Trigger: A test or wrapper that built roots = { project: '/a', team: '/b', user: '/c' } literally without the Symbol boundary map. JSON.parse(JSON.stringify(roots)) which drops the Symbol property. Object.assign({}, roots) which copies only enumerable keys. Spread {...roots} which also omits non-enumerable Symbol keys.

Common situations: Test fixture that hand-constructed a roots object. Wrapper that tried to 'clone and modify' roots via spread. Debug logging that serialized and re-parsed roots. Custom resolver that returned a plain object without calling the internal defineProperty for the boundary Symbol.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/6df8ad946346e872. Report an issue: GitHub.