Skip to content

Governance

Memory governance covers three things: who is allowed to read or write memory, how access is recorded, and how tenants are kept apart.

Tenant isolation

Hard. The agents SQLite database is per-Host, per-authority-root. A Host running for tenant A reads and writes its own <host-home>/data/agents/store.db — it has no path to tenant B's database. No cross-tenant query is possible from within the agent platform.

This is not the agent platform's invention; it inherits the bus-level authority root invariant. AgentsRoot.onBootstrap (AgentsRoot.ts:236-244) opens the database at <MATRIX_HOME>/data/agents/store.db, where MATRIX_HOME is set by the Host process for the active authority root.

Per-actor scope

The _resolveScope method on MemoryActor (MemoryActor.ts:115-122) decides which mount a memory write or read is on behalf of:

  • If the call comes from system.agents.<child>, the scope is the payload's mount (the child agent saving for the actor it serves).
  • If the call comes from cli or cli-gateway, the caller is admin and may set any mount.
  • Otherwise, the scope is the caller's own mount (from meta.from, the bus-set authenticated identity).

This guarantees that a non-admin caller cannot save a memory pretending to be a different actor.

Audit trail

memory_access_log (schema.sql:129-138) records every cross-actor memory access. Columns:

id, accessor_mount, target_mount, op, query, result_count, search_tier, ts

A read of own memory is also logged. memory.audit-trail returns the log scoped to a target mount with limit pagination (MemoryActor.accepts.['memory.audit-trail']).

The log is application-level — it does not replace system.observability traces. They are complementary: observability captures which actor invoked which op; audit captures what memory rows were touched.

Cascade enforcement

The cascade (see project memory) is enforced at query time by memoryBelongsToCascade (skills/MemoryCascade.ts). Every result must pass the test against the calling cascade query, which is built from the active profile. There is no path to bypass cascade — even an admin caller's reads are filtered by the cascade specified in the call.

What is NOT yet enforced

  • Per-tag deny lists. permissions.deny exists in ToolContextSpec (and is referenced in tool-permission docs) but there is no equivalent on memory queries.
  • Time-windowed scoping. "Only memories saved in the last 7 days" is not a first-class scope; callers must filter results client-side.
  • Encrypted memories at rest. The SQLite database is not encrypted at the column level; rely on filesystem-level disk encryption.
  • Per-principal quotas on memory writes. No write-rate limit; large agents could fill the database.

Operator controls

For administrative cleanup, the canonical surface is the $sessionClearAll op on AgentsRoot, which drops sessions, messages, and state — but does not delete memory rows or plans. Memory survives a session clear. To wipe memory entirely, drop the SQLite file (the schema is rebuilt on next boot).

See also

Source: projects/matrix-3/packages/agents/src/MemoryActor.ts:115-130 (scope resolution), projects/matrix-3/packages/agents/src/AgentsStore.ts:584-606 (audit log read/write), projects/matrix-3/packages/agents/schema.sql:129-140.