memcity

Enterprise

Audit Logging

Maintain an immutable audit trail of every operation for compliance, debugging, and security monitoring.

What is Audit Logging?

An audit log is an append-only record of everything that happens in your system — who did what, when, and what the result was. Think of it like a security camera for your data operations.

Unlike regular application logs that you might delete or rotate, audit logs are immutable — once written, they can never be modified or deleted. This is a requirement for compliance frameworks like SOC 2, HIPAA, and GDPR.

Why You Need It

Compliance

If your application handles sensitive data (healthcare records, financial information, personal data), auditors will ask: "Can you show me who accessed what data and when?" Memcity's audit log gives you that answer automatically.

Debugging

When something goes wrong in production — "Why did the search return wrong results for this user?" — the audit log shows you exactly which pipeline steps ran, what the query was, how many results were returned, and how long it took.

Security Monitoring

If someone is trying to access documents they shouldn't see (ACL denials), or if there's an unusual spike in API usage, the audit log captures it. You can build alerts on top of audit log queries.

How It Works

Automatic Recording

Once enabled, every operation is logged automatically — you don't need to add any logging code. Memcity instruments the pipeline internally.

ts
const memory = new Memory(components.memcity, {
  tier: "team",
  enterprise: {
    auditLog: true,  // That's it. Everything is now logged.
  },
});

What Gets Logged

Search Operations

Every getContext call is logged with:

json
{
  "action": "search",
  "timestamp": "2024-03-15T14:30:00.000Z",
  "actor": "user:alice",
  "orgId": "org_abc123",
  "knowledgeBaseId": "kb_xyz",
  "details": {
    "query": "What is the refund policy?",
    "queryType": "simple",
    "resultsCount": 5,
    "topScore": 0.94,
    "pipelineSteps": ["cache_miss", "embed", "semantic_search", "bm25_search", "rrf_fusion", "dedup", "rerank", "format"],
    "latencyMs": 342,
    "cached": false
  }
}

Ingestion Operations

Every document ingestion is logged:

json
{
  "action": "ingest",
  "timestamp": "2024-03-15T14:25:00.000Z",
  "actor": "system",
  "orgId": "org_abc123",
  "details": {
    "documentId": "doc_xyz",
    "source": "vacation-policy.md",
    "knowledgeBaseId": "kb_xyz",
    "chunksCreated": 12,
    "entitiesExtracted": 8,
    "processingTimeMs": 2340
  }
}

Deletion Operations

Document, knowledge base, and user deletions:

json
{
  "action": "delete_document",
  "timestamp": "2024-03-15T15:00:00.000Z",
  "actor": "user:bob",
  "orgId": "org_abc123",
  "details": {
    "documentId": "doc_xyz",
    "cascadeResults": {
      "chunksDeleted": 12,
      "entitiesOrphaned": 3,
      "edgesRemoved": 5,
      "storageFreed": true
    }
  }
}

ACL Changes

Every permission change is tracked:

json
{
  "action": "set_acl",
  "timestamp": "2024-03-15T14:45:00.000Z",
  "actor": "user:admin",
  "orgId": "org_abc123",
  "details": {
    "documentId": "doc_xyz",
    "previousPrincipals": ["group:hr"],
    "newPrincipals": ["group:hr", "group:engineering", "role:admin"]
  }
}

Access Denied Events

When ACLs block access, it's logged:

json
{
  "action": "access_denied",
  "timestamp": "2024-03-15T14:35:00.000Z",
  "actor": "user:charlie",
  "orgId": "org_abc123",
  "details": {
    "documentId": "doc_xyz",
    "userPrincipals": ["user:charlie", "group:engineering"],
    "requiredPrincipals": ["group:hr", "role:admin"],
    "query": "salary information"
  }
}

Querying the Audit Log

Basic Retrieval

ts
// Get recent audit log entries
const logs = await memory.getAuditLog(ctx, {
  orgId,
  limit: 50,
});
 
for (const entry of logs) {
  console.log(`${entry.timestamp} [${entry.action}] by ${entry.actor}`);
}

Filtering by Action Type

ts
// Only search operations
const searchLogs = await memory.getAuditLog(ctx, {
  orgId,
  action: "search",
  limit: 100,
});

Filtering by Time Range

ts
// Last 24 hours
const recentLogs = await memory.getAuditLog(ctx, {
  orgId,
  after: Date.now() - 24 * 60 * 60 * 1000,
  limit: 200,
});

Filtering by Actor

ts
// Everything a specific user did
const userLogs = await memory.getAuditLog(ctx, {
  orgId,
  actor: "user:alice",
  limit: 50,
});

Integration with ACLs

When both enterprise.acl and enterprise.auditLog are enabled, the audit log becomes especially powerful:

  • Access granted — Logged with which principals matched
  • Access denied — Logged with which principals the user had vs. which were required
  • ACL changes — Logged with before and after states

This gives you a complete picture of who accessed what data and who was blocked.

Retention and Storage

Audit logs are stored in Convex's database alongside your other data. They persist indefinitely — there is no automatic retention policy or rotation.

If you need to export audit logs to an external system (SIEM, data warehouse), you can query them programmatically and push to your preferred destination.

Storage impact: Each audit entry is typically 200-500 bytes. At 1,000 searches per day, that's ~500KB/day or ~15MB/month. Convex's storage is inexpensive, so this is a negligible cost.

Audit Log Entry Schema

FieldTypeDescription
actionstringOperation type: search, ingest, delete_document, set_acl, access_denied, set_quota
timestampstringISO 8601 timestamp
actorstringWho performed the action: user:xxx, system, or api_key:xxx
orgIdstringOrganization context
detailsobjectAction-specific data (query, results, timing, etc.)

Availability

Audit Logging is available on the Team tier only ($179 one-time).