Skip to content

Data contract

TokenShift has no behaviour-changing control plane. Customers configure the binary at install time via the local enrollment manifest deployed by their MDM; PointFive only ingests data and controls which release version is offered to a given client. See Installation & distribution for the install model.

Every record is hybrid-encrypted on the device against PointFive’s public key (pinned in the binary at build time) before it leaves the machine. Before anything is sent, sensitive content is removed on the device: known secret formats and the values inside the most sensitive fields are replaced with one-way hashes (commands keep their shape only). This page answers, in order: what leaves the device at all, what each tier controls, how long it’s kept, and what’s never sent under any configuration — with the full field-by-field detail as a reference at the bottom for anyone who needs it.


TokenShift sends two kinds of records, both hybrid-encrypted on the device before they ship:

  • Typed signals — five small, schema’d aggregates TokenShift computes itself (tool_invocation, recovery_retrieved, session_summary, compression_sample, client_state). Field-by-field detail: Reference → Typed signals.
  • Raw hook events — a Tier 2+ addition: the redacted payload each agent harness hands to its own hooks, forwarded under that hook’s native name (PreToolUse, UserPromptSubmit, afterShellExecution, and so on), plus context TokenShift derives locally (git branch, token counts, hostname, prompt intent). Field-by-field detail: Reference → Raw hook events and Reference → Enrichment.

Nothing else leaves the device. See What the contract does NOT send for the explicit list of content types that never ship in clear.


A single integer set by user choice and capped from above by install-time config. Effective tier on the wire is min(user_choice, admin_cap). Each higher tier strictly adds.

TierCLI mode nameWhat leaves the laptop
0(config telemetry disable, not a mode)Nothing. Local stats only.
1metricstool_invocation (no shape fields), recovery_retrieved, session_summary, client_state.
2patternsTier 1 + command_shape and rewritten_command_shape + raw hook events (redacted base + enrichment).
4samplesTier 2 + compression_sample. Triple-gated; off by default even when permitted.

Tier 3 is reserved internally and unreachable in practice — any attempt to set it silently clamps to Tier 2. The named modes are what tokenshift config telemetry mode <name> actually takes — see Core commands.

Default tier when enrolled: 2 (patterns). tokenshift enroll --tenant-id=<id> caps and emits at Tier 2 unless overridden with --tier-cap or local telemetry config.


Even with raw-event collection at Tier 2, the wire never carries any of the following in clear. The redactor removes them on the device before encryption:

  1. Bash stdout/stderr, tool output, prompts, messages, file contents — these field values are SHA-256 hashed on the device; only the hash is sent. The hash is stable (identical content correlates), but the content is not recoverable from it.
  2. Environment variable names or values.
  3. Raw file-path values — path / cwd / transcript-path fields are hashed.
  4. Raw command bytes — commands ship as redacted shape only.
  5. OS usernames, real names, IP addresses, MAC addresses.
  6. Raw email — read once locally to compute user_id, then discarded.
  7. Rule contents — only the rule’s identifier.
  8. Commit messages.
  9. Prompt text used for intent classification — only the resulting label (e.g. “debug,” “feature work”) ships, via enrichment.

For completeness, what the wire does carry from the above: stable SHA-256 hashes of those content fields (for correlation, never recovery), and — via enrichment — git context (branch name, repo remote URL, PR number), machine hostname, and prompt-intent labels.


RecordDefault retentionTenant-extensible?
tool_invocation90 daysYes
recovery_retrieved90 daysYes
session_summary90 daysYes
client_state90 daysYes
Raw hook events90 daysYes
compression_sample7 daysNo — hard cap.

The 7-day hard cap on compression_sample is non-negotiable. Samples are the only signal that carries redacted output content; the rule-tuning use case doesn’t benefit from longer retention, and every extra day is unnecessary blast radius.


Everything below is the detailed, field-by-field version of what’s summarized above — useful for a full audit, not required reading to understand the contract.

TokenShift computes five typed aggregate records:

#SignalMin tierFires when
1tool_invocation1The agent makes a tool call.
2recovery_retrieved1A developer pulls back output that was compressed away.
3session_summary1An agent session ends.
4compression_sample4Triple-gated, off by default — a redacted before/after pair for rule tuning.
5client_state1Periodically (≤ once / 24h per machine) — version + install metadata.

tool_invocation — one per tool call, regardless of whether TokenShift compressed it. Carries tool kind, agent (which harness — including codex now), model (raw model string included), a tool-call join id, tool duration, activity bucket, project (raw directory name by default; can be hashed), a stable command_hash, original token count, and an outcome enum. At Tier 2+ also carries redacted command_shape and rewritten_command_shape. No raw command bytes ship at any tier.

recovery_retrieved — fires when a developer pulls back the original output via the recovery command. Carries only a join key, the recovering session, and an age in seconds. No content. No file paths. Note: the recovery cache itself isn’t available on every harness — see Recovery cache scope below — so this signal simply never fires from a Cursor or Codex client.

session_summary — one best-effort record per session at session end. Carries end_reason, turn count, peak context-window usage, and small denormalized aggregates (tokens in, tokens saved, per-outcome counts).

compression_sample — a redacted before/after pair for PointFive rule tuning. The only signal that carries actual output content — and the most tightly gated record in the contract. Off by default. Three independent gates must all be true: tenant has enabled Tier 4, the matched rule is on the per-rule sample allowlist, and a random draw lands inside the configured sampling percentage. Carries no user_id (defense in depth). Hard-capped retention: 7 days.

client_state — periodic meta-telemetry about the binary itself: version, OS, architecture, install method, enrollment age, and whether the startup self-check passed. Rate-limited to at most once per 24 hours per client_id. No PII, no per-tool-call detail. This is how PointFive and admins see fleet hygiene.

Raw hook events are a Tier 2 addition: at Tier 2 and above, TokenShift also forwards the hook events its supported agents emit — the same payloads the harness hands to any hook — after redaction. Each becomes one record whose signal name is the native hook event (e.g. PreToolUse, UserPromptSubmit, afterShellExecution). Tiers 0 and 1 do not include them.

Coverage by agent (four harnesses today — Claude Code, Cursor, GitHub Copilot CLI, and Codex):

AgentHook events forwarded
Claude CodePreToolUse, PostToolUse, PostToolUseFailure, SessionStart, SessionEnd, Stop, StopFailure, UserPromptSubmit, UserPromptExpansion, PreCompact, PostCompact, PostToolBatch, SubagentStart, SubagentStop, PermissionRequest, PermissionDenied, Notification, Setup, InstructionsLoaded, MessageDisplay, TaskCreated, TaskCompleted, TeammateIdle, ConfigChange, CwdChanged, FileChanged, Elicitation, ElicitationResult
CursorbeforeShellExecution, afterShellExecution, beforeMCPExecution, afterMCPExecution, beforeReadFile, afterFileEdit, beforeSubmitPrompt, sessionStart, sessionEnd, preCompact, stop, afterAgentResponse, afterAgentThought, subagentStart, subagentStop, postToolUseFailure, workspaceOpen
GitHub Copilot CLIpreToolUse, postToolUse, postToolUseFailure, userPromptSubmit, sessionStart, sessionEnd, errorOccurred, permissionRequest, preCompact, agentStop, subagentStart, subagentStop, notification
CodexSessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PermissionRequest, PreCompact, PostCompact, SubagentStart, SubagentStop, Stop

Claude Code’s surface is the largest and grows fastest — it’s the harness with the richest native hook set, and TokenShift forwards nearly all of it (two internal delegation hooks are excluded on purpose). Codex speaks a Claude-shaped protocol but exposes a smaller hook surface today — notably no native SessionEnd.

Each raw event carries:

  • base — the harness’s hook payload, redacted on the device (see Redaction). Fields the harness includes that aren’t sensitive pass through as-is.
  • enrichment — context TokenShift derives locally (see Enrichment).
  • Dimensionstenant_id, client_id, agent, session_id, binary version, and a timestamp.

Raw hook events are scoped to client_id, not user_id — they carry no pseudonymous user identifier.

tokenshift recover <hash> only works when the compressing proxy runs unsandboxed and can write to the local ~/.tokenshift/recovery cache:

AgentProxy sandboxed?tokenshift recover works?
Claude CodeNoYes
GitHub Copilot CLINoYes
CursorYesNo
CodexYesNo

Cursor and Codex both run the compressing proxy inside a sandbox that can’t write outside the workspace, so the recovery cache write silently fails and no [Full output ...: tokenshift recover <hash>] hint is ever emitted for those sessions — compression still happens, the saved-output pointer just isn’t available. Compression stats for both still ship normally, handed off through a short-lived workspace-local file instead of being recorded in-process. See the Harness coverage matrix for how this factors into each harness’s score.

For some events TokenShift computes extra context on the device and attaches it under enrichment. It carries no identity data.

EnricherRuns onSends
gitsession start, prompt submitbranch (current branch name), repo (origin remote URL with any embedded credentials stripped), and — on prompt submit — pr (current PR number, via gh)
tokensprompt submit, tool-result events, agent-response events, turn endinput_tokens / output_tokens (no content). Tokens are labelled from the agent’s perspective: text fed into the agent’s context — the user prompt and tool results (observations) — is input; text the agent generates — its replies and reasoning — is output. A source field marks each count as estimate (a per-payload approximation) or transcript (an authoritative count parsed from the harness’s own session log). Claude Code, Copilot CLI, and Codex each read their own harness-specific log format; Cursor has no token enricher today, so it carries no reconciled count at all.
hostnamesession startThe machine’s hostname — used for fleet-hygiene context, not tied to a person.
intentprompt submitA locally-computed classification of what the prompt is trying to do (e.g. debug, feature work, refactor), produced by a small on-device model — see How it works. The classification label ships, never the prompt text itself.

Every knob a tenant admin sets when deploying TokenShift via MDM. There is no runtime config push — changes require regenerating the manifest from the PointFive app and redeploying via MDM. No user-level overrides.

ControlDefaultEffect
Tier capTier 2Maximum tier any client in the tenant can emit.
Project identifier modeRaw directory nameOr hashed directory name, or hashed git remote URL.
Recovery cache enabledOnOff disables the local short-lived cache (already unavailable by default on Cursor/Codex — see Recovery cache scope).
Sample allowlistEmptyPer-rule list of which rules may be sampled at Tier 4. Empty = no samples ever emitted.
Sample rate0%Per-rule sampling probability used at Tier 4.
Retention extensionsDefaults aboveNegotiated per tenant. compression_sample is hard-capped regardless.
  • tenant_id — customer organization, from the enrollment manifest.

  • client_id — random UUID created on first run and persisted under ~/.tokenshift/. Reused across runs, survives upgrades and re-enrollment. One per OS user account. Present on every record, including raw hook events.

  • user_id — pseudonymous user identifier derived locally as a one-way hash of the developer’s email under a per-tenant key:

    user_id_hmac_key (random secret, generated at enrollment, lives in manifest)
    email ─── one-way hash ──▶ user_id (ships on the wire)
    ▲ email is discarded immediately;
    │ never persisted, never sent.
    git config --global user.email
    (fallback: $TOKENSHIFT_USER_EMAIL; otherwise user_id = null)

    Same email + same tenant key → identical user_id on every machine that developer uses, enabling cross-machine joins. user_id is present on the typed signals (except compression_sample and client_state) and absent from raw hook events.

  • project — raw directory name by default; install-time config may switch to hashed directory name or hashed git remote URL.

  • session_id, invocation_id, recovery_id — opaque UUIDs used as join keys between signals.

Raw email, OS usernames, IPs, MACs, and raw file-path values are never sent in clear — file-path fields are among the hashed sensitive groups (see Redaction). The git repo URL and branch name are sent via enrichment as described above.

Redaction runs on the device, before encryption, over both the typed signals’ shape fields and every raw hook event’s base. Every field TokenShift might ever emit is typed to a data group (prompt text, file contents, command, file path, URL, identifier, and so on), and a per-group actionRemove, Hash, Redact (kept as a shape, not raw), or Pass — is applied at the effective tier:

  • Content and I/O fields (prompt, command, content, output, stdout, stderr, tool_input, tool_response, result, message, …), search/egress inputs (query, pattern, url, …), identifying paths (file_path, path, cwd, transcript_path, …), and PII (user_email) all have their values replaced before send.
    • command is shape-redacted: structure kept, argument values stripped (its shape carries analytics value).
    • Every other sensitive value is replaced with a SHA-256 hash. The hash ships; the original does not.
  • Known secret formats are stripped wherever they appear.
  • Fields not modeled into a sensitive data group pass through as sent by the harness. Only modeled fields ship at all — an unmodeled field or event drops rather than shipping unredacted by accident.

A startup self-check runs representative inputs through the redactor; if it fails, telemetry is disabled for that process (fail-closed).

Anything that doesn’t go right ends in a drop or a disabled producer, never a raw emit.

TriggerAction
Redactor startup self-check failsTelemetry disabled for the process — nothing sent.
Envelope tampered (tenant / signal / schema mismatch in the bound headers)Rejected at decrypt (HTTP 400); never persisted.
Encryption / seal failsRecord dropped.
Tier below a record’s minimumNot emitted.
Sample allowlist or sample-rate missSample not emitted.
Local outbox overflowOldest records dropped first; the developer’s command is never blocked.
Network unavailableRecords queued locally until network returns.
Enrollment manifest absent or unparseableTelemetry off; nothing sent.
  • Typed signal — a named, schema’d aggregate record (one of the five above).
  • Raw hook event — a redacted copy of a hook payload an agent harness emits, forwarded under the hook’s own event name (Tier 2+).
  • Enrichment — non-identity context TokenShift derives locally (git, token estimates, hostname, prompt intent) and attaches to raw events.
  • Redaction — device-side removal of sensitive field values (SHA-256 hash, or shape for commands) before encryption, driven by a per-field data-group classification.
  • Fail-closed — on any error, drop the data (or disable the producer) rather than emit it raw.
  • Tier — level of data sharing (0/1/2/4). Capped from above by install-time config.