Multi-tenant agent deployments

August 16, 2026 ยท View on GitHub

agent-strace can tag sessions and events with an application customer ID, scope queries and costs to that ID, and perform customer export and erasure workflows. Tenant tags are additive: sessions recorded before v0.90.0 remain readable and appear as (untagged).

Tag every customer session

Assign the tenant when monitoring an active session:

agent-strace watch --tenant-id customer-acme

For hooks, CI, auto-instrumentation, or other non-interactive capture paths, set the environment variable before the agent starts:

export AGENT_STRACE_TENANT_ID=customer-acme
python my_agent.py

tenant_id is stored at the top level of meta.json and every event in events.ndjson. The session value is authoritative: agent-strace rejects an event with a different tenant and refuses to move an already-tagged session to another tenant. When watch tags events already written to the active session, it atomically rewrites the file and rebuilds the SHA-256 event chain. The pre-change and post-change terminal hashes are recorded in .agent-traces/tenant-audit.ndjson. A small durable intent journal is used so an interrupted metadata/event transition can roll forward on restart.

New session and workspace IDs accepted by capture and collector entry points use a strict 1โ€“128 character ASCII format (A-Z, a-z, digits, ., _, and -). Existing safe directory basenames remain readable, reportable, exportable, and deletable even when they contain spaces, Unicode, or exceed 128 characters. Legacy names must still be a single path component: path separators, dot segments, NUL/control characters, and symlink escapes are rejected.

Parent and child sessions must resolve inside the same flat/workspace store and have the same tenant. A newly created untagged child inherits a tagged parent's tenant. Legacy untagged parent/child pairs remain readable, but tagging either side is rejected if it would leave the relationship split between tagged and untagged (or differently tagged) sessions. For a legacy untagged tree, unlink the relationship, tag both sessions, and then relink it as one coordinated migration.

Use stable opaque customer identifiers rather than names, emails, or other personal data. Tenant IDs must be 256 characters or fewer and cannot contain control characters.

Scoped queries and cost attribution

# Only this customer's sessions
agent-strace list --tenant customer-acme

# Estimated cost over the last 30 days
agent-strace cost --tenant customer-acme --since 30d

# Actual recorded provider/model counters for the same tenant
agent-strace cost --tenant customer-acme --breakdown provider --since 30d

# Cost allocation across every tenant for one UTC month
agent-strace tenant report --month 2026-06
agent-strace tenant report --month 2026-06 --format json

The standard estimate uses the selected offline pricing model. Provider breakdowns use recorded model/token counters and the dated bundled pricing snapshot. Both perform exact tenant matching before event data is loaded.

Tenant administration uses fail-closed store discovery. Unlike the ordinary interactive session list, report, export, and deletion abort when a plausible session directory has malformed metadata/events, missing core files, unsafe symlinks, or a directory/metadata ID mismatch. Workspace discovery likewise aborts on symlinked or unsafe workspace roots and children, so an omitted store cannot produce a false zero report or false successful erasure.

Subject access export

Export all metadata and events for one customer as a single JSON document:

agent-strace tenant export customer-acme > customer-acme.json
# or
agent-strace tenant export customer-acme --output customer-acme.json

The versioned export contains tenant_id, exported_at, session_count, and a sessions array with complete metadata, event history, session sidecars, and compaction checkpoints. Its versioned external_records section also contains matching human-approval requests (including tool input), eval-dataset entries, and retention-log records. It searches the flat store and every workspace store, regardless of AGENT_STRACE_WORKSPACE, and includes relevant hash-only tenant audit evidence. Review the output's storage location and access controls before sending it to a data subject.

Right to erasure

Preview the customer ID carefully, create an access export if required by your policy, and then run the explicitly confirmed deletion:

agent-strace tenant delete customer-acme --confirm

This is irreversible. It removes every matching session directory, including annotations, evals, identities, postmortems, and other files stored within it, matching compaction checkpoints, approval requests, dataset entries, and retention-log records. Shared JSONL files are atomically filtered so malformed and unrelated records remain unchanged. Other tenants and untagged sessions are untouched.

Before removing data, the command fsyncs a hash-only pending record to .agent-traces/tenant-deletions.ndjson; it appends completed or partial afterward. Tenant IDs and session IDs are never stored there in plaintext. Hashed session IDs act as tombstones so late hook events cannot recreate erased trace data. A recoverable intent journal finishes interrupted deletions during ordinary TraceStore startup as well as tenant administration commands. The operation covers the flat store and every workspace store and clears matching workspace hook state. Hook markers and pending-call files live beneath their workspace store. Provider IDs are represented by a path-safe local-session component and SHA-256 digest rather than being copied into filenames. Legacy root markers are removed only when no surviving flat or workspace session makes their ownership ambiguous; markerless pending state is removed only when its safe suffix identifies an erased session.

Export and deletion reject symlinked session metadata, event streams, checkpoint directories/files, audit files, and external-record sources. This fail-closed behavior prevents a trace store from reading or deleting a target outside its configured root.

Tenant audit files and completed journal directories are outside normal session retention. Set an explicit operational retention/rotation schedule for tenant-audit.ndjson and tenant-deletions.ndjson based on legal advice and backup policy. Incomplete files under .tenant-journal/ must not be deleted until recovery succeeds or an operator has reconciled the recorded intent.

Isolation and hosted collectors

Tenant tags scope agent-strace queries; they do not replace operating-system permissions or collector authorization. Anyone who can read the trace directory can read its NDJSON files, and anyone who can run local commands against it can select another tenant ID.

The built-in collector's bearer key protects a collector instance, which must be treated as the organization boundary; it is not an individual-tenant key. For a hard hosted multi-organization boundary, deploy a separate storage directory/collector per organization or place each collector behind an authenticated gateway that derives organization scope from server-side key metadata. Never accept an organization ID supplied by the client as the authorization decision. Within an authorized organization, tenant_id can be used as the customer-level tag.

OTLP and live --stream-otlp exports attach tenant_id to every generated span and the OTLP resource. Configure equivalent access controls and retention at the observability backend.