Privacy-native policy primitives for audience-scoped AI agent memory.
paperboy-memory decides which memory stores an agent may access for one conversation run. It binds every run to an authoritative audience snapshot, fails closed when that audience changes, and makes private-to-public disclosure an explicit, auditable operation.
Important
This is a policy kernel, not a complete memory database and not a security certification. Your adapter remains responsible for authoritative identity data, transactional enforcement, encryption, storage isolation, and audit retention.
Most agent-memory libraries start with retrieval and add privacy later. This project starts with the privacy boundary:
- classify the audience before loading memory;
- bind access to a short-lived
PrivacyRun; - expose private stores only when the owner is effectively alone with their own agents;
- revalidate the audience inside the same critical section as each write;
- require reviewed text and owner approval for private-to-public publication;
- consume ad hoc disclosure approval exactly once.
v0.1.0 is an early policy-core release. The API may change before v1.0.0. It has no telemetry, network calls, persistence engine, or Paperboy service dependency.
Install the release artifact directly from GitHub:
npm install https://github.com/paperboytm/paperboy-memory/releases/download/v0.1.0/paperboy-memory-0.1.0.tgzThe package is ESM-only and requires Node.js 22 or newer. It is not currently published to the npm registry.
import {
authorizeMemoryAccess,
classifyAudience,
issuePrivacyRun,
} from "paperboy-memory";
const audience = classifyAudience({
conversationId: "conversation_123",
ownerUserId: "user_alice",
surface: { kind: "direct" },
members: [
{ id: "user_alice", kind: "human", active: true },
{
id: "agent_helper",
kind: "agent",
ownerUserId: "user_alice",
active: true,
},
],
});
const run = issuePrivacyRun({ agentId: "agent_helper", audience });
// Obtain this again from your authoritative backend immediately before I/O.
const currentAudience = audience;
const access = authorizeMemoryAccess({
run,
agentId: "agent_helper",
conversationId: "conversation_123",
currentAudience,
});
if (!access.ok) throw new Error(`${access.code}: ${access.message}`);
// Read/write only access.stores, with this call in the same transaction
// or critical section as the operation.
console.log(access.stores);See examples/basic.ts for the complete example.
| Conversation context | Stores exposed |
|---|---|
| Owner alone with their own agents | user-private, agent-private, user-public, agent-public |
| Direct message with anyone else | user-public, agent-public |
| Group conversation | user-public, agent-public |
| Workspace channel | user-public, agent-public, agent's server-shared store |
| Restricted channel | user-public, agent-public |
| Unresolvable audience | no PrivacyRun is issued |
Unknown, external, foreign, and ownerless principals are treated as shared. An owner-controlled observer agent may be excluded from the audience; a foreign observer may not.
classifyAudience— turns authoritative membership and scope into an immutable classification and fingerprint.issuePrivacyRun— binds an agent, owner, conversation, scope, audience, and expiry.authorizeMemoryAccess— revalidates a fresh audience and returns stores only on success; use this at I/O boundaries.evaluateRunGate— revalidates the binding for custom adapters and higher-level operations.resolveAllowedMemoryStores/isStoreAllowed— applies only the pure store matrix after a run has already been validated.memoryStoreKey— creates a validated logical key, never a filesystem path.preparePublicPublication— creates a separate public document from owner-reviewed text without mutating its private source; sensitive lineage is returned as a separate audit.decideDisclosure— approves or declines one exact, short-lived disclosure; approval cannot be reused.
The library cannot enforce these guarantees on its own:
- Membership, ownership, channel access, and conversation identity come from an authoritative backend, never from the model or client.
PrivacyRunis created and retained on the trusted server; it is never accepted from a client or model as a token.- A fresh audience is loaded and
evaluateRunGateis called in the same transaction or critical section as every write. - Logical store keys are mapped below a fixed storage root with backend-appropriate traversal, symlink, ACL, and tenant-isolation protections.
- A disclosure message insert and transition to
consumedcommit atomically. - Publication approval and the new public document commit atomically; the immutable audit is stored outside public retrieval.
- Content safety, retention, deletion, encryption, and regulatory obligations are implemented by the host application.
Read the architecture, threat model, and security policy before integrating.
npm ci
npm run check
npm run test:coverageContributions are welcome. See CONTRIBUTING.md, GOVERNANCE.md, and CODE_OF_CONDUCT.md.