Skip to content

TokenShift Enterprise — Installation, Distribution, and Update

Audience: customer admins deploying TokenShift via MDM (Jamf, JumpCloud, Intune, Ansible, etc.) and security reviewers evaluating the install model. Companion to: the Data contract — describes what TokenShift emits once installed.


TokenShift has no control plane. Customers control the binary entirely:

  • Distributed through standard package channels (Homebrew, apt, yum, MSI) and pushed by the customer’s MDM.
  • Bound to a tenant via a one-time enrollment manifest — four fields of plain JSON, downloaded from the PointFive customer portal, deployed by MDM.
  • All ingest data is hybrid-encrypted client-side with PointFive’s public key (pinned in the binary, not the manifest); only PointFive’s KMS holds the matching private key.
  • Server-side ingest is stateless: HTTPS endpoint → decrypt → persist. No intermediate storage, no brokers.
  • Updates happen at the customer’s pace via MDM. PointFive cannot push updates, configuration, or rules to running clients.

The enrollment manifest is deliberately tiny — four fields, no signatures, no expiration, no user roster. The customer admin’s day-1 task is “click generate, drop the JSON in MDM.”


A remote control plane is the highest-value attack surface in any client agent: compromise it and you compromise every endpoint at once. Customers also rightly ask “what stops you from pushing a malicious config to my fleet?” By eliminating the control plane we remove that risk class entirely.

The cost: customers drive their own upgrades and reconfigurations through MDM. We accept the trade.

This means:

  • No remote policy push. All config is install-time, baked into the enrollment manifest deployed by MDM.
  • No remote rule update. Rules ship in the binary. New rules require a new binary.
  • No remote opt-out. A user opts out by changing local config (subject to admin cap); admin opts out by redeploying the manifest or uninstalling via MDM.
  • No audit-event stream. All “who changed what” lives in the customer’s MDM audit log, not in TokenShift telemetry.

The binary’s behavior is fully determined by its bundled rules + the enrollment manifest. The only feedback channel from PointFive is the data ingest pipeline (one direction only).


PlatformPrimary channelMDM integration
macOSHomebrew tap (signed .pkg also available)Jamf, Mosyle, Kandji, JumpCloud — push brew or .pkg
Linux (Debian/Ubuntu)apt repository (.deb packages)Ansible, Salt, Chef, JumpCloud, SCCM
Linux (RHEL/Fedora/Amazon)yum repository (.rpm packages)same
WindowsMSI installerGroup Policy, Intune, Microsoft Configuration Manager
Direct downloadGitHub Releases (signed archives + checksums)Manual or scripted install

Each release produces cross-compiled artifacts for linux/darwin/windows × amd64/arm64, plus:

  • Signed .pkg for macOS (Apple Developer ID + notarization).
  • Signed MSI for Windows (EV Code Signing cert).
  • .deb and .rpm packages for the Linux repos.
  • A signed release-manifest.json listing checksums and version metadata.

Required for MDM trust chains and platform security gates:

  • macOS — Apple Developer ID Application certificate; notarization via Apple’s notary service. Without notarization Gatekeeper blocks the binary.
  • Windows — EV Code Signing certificate. Without it SmartScreen warns users and Intune cannot deploy the MSI without per-machine bypasses.
  • Linux — Repository GPG signing for apt and yum.

Signing keys live in PointFive’s CI vault, accessible only to the release pipeline; never on developer laptops.


The one-time setup that binds an install to a tenant. Designed to be as low-friction as possible: the customer admin’s job is to download a tiny JSON file and drop it into MDM.

  1. Logs into the PointFive customer portal via the customer’s SSO.

  2. Confirms tenant_id (allocated when the customer onboarded).

  3. Picks install-time configuration from a form: tier cap, project hash mode, recovery cache enable, sample allowlist, sample rate, retention extensions.

  4. Clicks Generate manifest.

  5. Portal generates a fresh bearer_token and a fresh per-tenant user_id_hmac_key (32 bytes from the portal’s CSPRNG).

  6. Portal returns the manifest as enrollment.json.

  7. Customer admin downloads the file. Two delivery shapes, pick either:

    • (MDM, system-wide — recommended for managed fleets) Deploy via MDM as root to the system path:

      • macOS / Linux: /etc/tokenshift/enrollment.json
      • Windows: %PROGRAMDATA%\tokenshift\enrollment.json

      One file, one deployment unit, covers every user on the machine. This is the natural shape for Jamf, Intune, JumpCloud, Ansible — they all run as root/SYSTEM and deploy to system paths trivially.

    • (Self-serve, per-user) Email/Slack the manifest to developers and have them run tokenshift enroll <path>. Writes to ~/.tokenshift/enrollment.json. No sudo, no system path, per-user.

    The binary searches per-user first, then system, so both delivery paths work. A developer can run tokenshift enroll even on a managed endpoint with an MDM-deployed system manifest — the per-user file will shadow it and tokenshift doctor will warn about the override.

  8. MDM is also configured to push the binary install via the appropriate package channel.

That’s it. No user roster export. No SSO-and-roster reconciliation. No expiration calendar entry. No signature chain to manage.

For self-serve installs (and dev machines), the CLI provides:

tokenshift enroll <path> # copy a file into ~/.tokenshift/enrollment.json
tokenshift enroll - # read stdin (paste / pipe)
tokenshift enroll --dry-run … # validate without writing
tokenshift enroll --force … # overwrite an existing manifest (deliberate)

The command validates the JSON against the manifest schema (§4.4) before persisting, writes with 0600 to a 0700 parent directory, and never overwrites silently. After enrollment, tokenshift doctor --verify ships one synthetic record through the live wire path to confirm pinning + manifest + ingest are all in agreement.

  1. Reads the enrollment manifest by searching, in order: $TOKENSHIFT_ENROLLMENT_PATH (dev/test override), then ~/.tokenshift/enrollment.json (per-user — written by tokenshift enroll), then the system path (/etc/tokenshift/enrollment.json or %PROGRAMDATA%\tokenshift\enrollment.json — deployed by MDM).
  2. Parses it. If absent or malformed → fail-closed (logs error, hooks exit 0 so Claude Code is unaffected).
  3. Generates a random client_id UUID and writes it to /var/lib/tokenshift/client.json (machine-level, persists across upgrades and re-enrollment).
  4. Resolves the local user’s email via the fallback chain (see §4.3).
  5. Computes user_id = HMAC(manifest.user_id_hmac_key, email) locally. If no email source is available, user_id is null and emitted records simply omit the field.
  6. Caches tenant_id, bearer_token, user_id_hmac_key, and config in memory.
  7. Emits its first record: a client_state heartbeat. Hybrid-encrypted client-side against PointFive’s public key pinned in the binary (compiled in at build time, not from the manifest), POSTed to the pinned ingest URL (also compiled in).

Run silently in this order; first hit wins:

OrderSourceNotes
1git config --global user.emailPrimary. Set on essentially every developer machine — git refuses to commit without it.
2$TOKENSHIFT_USER_EMAIL env varEscape hatch the customer admin can populate via MDM if needed.
3Noneuser_id is null on emitted records. Analytics degrade to per-machine attribution via client_id.

The binary reads the email once, computes the HMAC, stores the resulting hash next to client_id, and never reads the email again. The raw email is not stored, not cached, not sent on the wire.

{
"tenant_id": "acme-corp",
"bearer_token": "Bearer abc123def456",
"user_id_hmac_key": "base64-encoded-32-bytes",
"config": {
"tier_cap": 2,
"project_mode": "hashed_remote",
"recovery_cache_enabled": true,
"sample_allowlist": [],
"sample_rate": 0.0
}
}

Four top-level fields. No signature, no expiration, no user roster, no public key in the manifest, no ingest URL in the manifest.

Why so simple:

  • No signature. The only things in the manifest that could matter to an attacker (public key, ingest URL) are pinned in the binary, not the manifest. Tampering with the manifest can at most: change config (DoS of telemetry, not a security breach) or redirect records (which the attacker can’t decrypt).
  • No expiration. Bearer-token rotation handles compromise. No calendar reminders, no rolling redeployments, no expired-manifest outages.
  • No user roster. Emails are resolved locally on each machine — see §4.3.
  • No public key. PointFive’s ingest key is pinned in the binary at build time. Key rotation = ship a new binary version (binaries can ship multiple acceptable keys for graceful rotation).
  • No ingest URL. Hardcoded as https://ingest.tokenshift.io/v1. Regional routing variants would be separate binary builds — not a runtime configuration.

Three reasons it might change in steady state:

  1. Config change — admin wants to raise the tier cap, enable Tier 4 sampling on a specific rule, etc. Regenerate from portal, redeploy via MDM.
  2. Bearer token rotation — admin suspects compromise or rotates on schedule. Regenerate from portal (new token, all other fields can stay), redeploy via MDM. Old token revoked server-side.
  3. HMAC key rotation — rare, used only if a customer machine is compromised and the key may be exposed. Regenerate from portal, redeploy. All user_ids change — historical data still carries the old user_ids, post-rotation data has new ones. Customer’s call when to take this hit.

The binary re-reads the manifest at next session start. No mid-session reload.


5.1 Why client-side encryption on top of HTTPS

Section titled “5.1 Why client-side encryption on top of HTTPS”

HTTPS protects in transit only. Without client-side encryption, anyone in PointFive’s ingest path (operators, infrastructure providers, anyone with read access to log aggregation, anyone exploiting a service-side bug) would see plaintext. With client-side encryption, every record is opaque from the moment it leaves the laptop until the ingest endpoint’s decryption step. A breach of any other surface — load balancer, log aggregator, bastion host — yields nothing.

Hybrid encryption per record. The client uses PointFive’s public key, pinned in the binary at build time, to encrypt a fresh symmetric key for each record; the payload is sealed with that symmetric key. The client only ever holds the pinned public key.

  • Compromise of a client laptop yields ciphertext but no decryption capability.
  • Compromise of the manifest grants no decryption capability either — the public key isn’t in the manifest.

5.3 Key management — pinned public key, customer can’t reach the private key

Section titled “5.3 Key management — pinned public key, customer can’t reach the private key”
  • One PointFive-wide keypair. Public key compiled into every binary at build time; never carried in the manifest, never fetched at runtime.
  • The private key lives only in PointFive’s key-management system, with audited access and no plaintext extraction.
  • Rotation: when PointFive rotates, a new binary version ships with both keys pinned (current + next). Customers upgrade at their pace; both keys remain valid for ingest during the transition. Once enough customers have upgraded past the version that introduced the new key, the old key is retired.

Key rotation requires a binary version that customers must adopt — not a manifest redeploy. Slower than a portal-side rotation, but key rotations are rare (annual) and customers are upgrading binaries regularly anyway. The trade is “rotation cadence is bounded by customer upgrade willingness” against “manifest tampering grants no decryption capability.” We chose the latter.

Per-tenant blast-radius isolation. With one PointFive-wide keypair, a private-key compromise exposes all tenants’ in-flight ciphertext until rotation. The compensating control is the key-management posture: no plaintext extraction, audited access. If a single tenant ever requires per-tenant keys (regulated industry, data-residency contract), that’s an additional engineering effort — not the default.


End-to-end:

Client PointFive ingest
────── ────────────────
encrypt(record) → HTTPS POST → stateless receiver
└─ decrypt
└─ allowlist-check
└─ persist (one signal type → one stream)
└─ 200 OK

Key properties:

  • Stateless ingest service. No intermediate storage, no broker, no records at rest in the ingest path. Each request is persisted before the response returns (or is rejected and the response says so).
  • One stream per signal. tool_invocation, recovery_retrieved, session_summary, compression_sample, client_state — five typed streams, never mixed.
  • Schema enforcement. Malformed rows are rejected at persistence time and surfaced to the ingest service.
  • No retries by the service. If persistence fails, the service returns a non-2xx; the client re-enqueues the record in its local outbox.

No automatic updates. Period.

  • Customer admins push new versions via MDM at their own cadence.
  • Homebrew users run brew upgrade tokenshift (admin-initiated via Jamf or similar).
  • The client_state signal (Data contract §5) reports the running binary version; PointFive (and customer admins) see staleness in dashboards.

We do not ship an in-process updater. Putting the upgrade decision behind the customer’s MDM is intentional — it preserves the “no remote control” property and aligns with how customers want to govern their fleet.


Customers will routinely run multiple versions in their fleet. The backend supports the last 6 minor versions of each signal’s schema by default. Older versions are rejected at ingest with 426 Upgrade Required.

  • The 6-version window is extendable on request, per tenant.
  • Stale endpoints are visible to admins via stalled client_state rows and (when records are being rejected) via the absence of recent tool_invocation rows for affected client_ids.
  • Sunset is the customer’s lever, not PointFive’s. We refuse old versions; the customer admin sees data dry up and pushes the upgrade.

This is the correct pressure model: PointFive cannot force, the customer holds the operational lever, and dashboards make staleness visible.


ThreatMitigation
Malicious tampered binary substituted for the legitimate oneCode signing on every platform; MDM enforces signature verification before install.
Malicious enrollment manifest substitutedMaximum impact: attacker changes config (DoS of telemetry), redirects records (which they can’t decrypt), or invalidates tokens. Public key and ingest URL are pinned in the binary, so manifest tampering grants no decryption capability and no exfiltration path.
Stolen laptop’s enrollment manifest exfiltratedAttacker has bearer token + tenant_id + HMAC key. They can encrypt and send fake records as that tenant (DoS / poisoning the customer’s dashboards) and they can compute user_id for any email they guess — but they had to compromise a customer machine first, at which point much more sensitive data is also accessible on that machine. Mitigation: rotate bearer token and HMAC key on suspected leak.
Network MITMTLS to a pinned set of PointFive root certificates; ingest URL is pinned in the binary. Hybrid encryption means even a successful MITM gets ciphertext only.
PointFive ingest breachPer-record ciphertext; an attacker without access to the private key gets no decryption capability. The ingest service is stateless — no records at rest in PointFive’s ingest path.
PointFive key-management breachWorst case — private key extracted means in-flight data decryptable until rotation. Compensated by the key-management posture (no plaintext extraction, audited access). Rotation requires a binary release; customers may run unrotated keys for the rotation window.
PointFive release-signing key compromiseAttacker can sign malicious binaries. Mitigated by the OS-level code-signing chains (Apple Developer ID, EV cert) — those are independent of PointFive’s keys, controlled by Apple/Microsoft. An attacker would need to compromise both.
Stale binary running known-vulnerable codeclient_state reports version; admin sees staleness and drives upgrade. Refusing to ingest very old versions puts upward pressure on the chain.
Developer machine has no git config user.emailuser_id is null on emissions; analytics degrade gracefully to per-machine. No security impact.
Attacker tries to reverse user_id from leaked PointFive dataWithout the per-tenant HMAC key (which lives in customer manifests, not on PointFive’s side), unreversible. With access to a manifest, reversible if the email is guessable — but at that point the customer’s perimeter is already breached.

10. What’s deliberately not in this design

Section titled “10. What’s deliberately not in this design”
  • Auto-update. Pushes the upgrade decision to the client; customers want explicit control.
  • Remote configuration push. No control plane.
  • Manifest signing. Public key and ingest URL are pinned in the binary; manifest tampering grants no decryption capability or exfiltration. Signing would only protect config integrity, which is far less critical than the encryption story.
  • Manifest expiration. Bearer-token rotation handles compromise. Expirations create operational burden for marginal additional security.
  • User roster in manifest. Replaced by local resolution from git config user.email. Eliminates ongoing roster maintenance and keeps emails off developer disks.
  • Live user-roster sync. New hires enroll on first Claude session. Departures stop emitting (and admin uninstalls via MDM).
  • Per-tenant encryption keypairs. Rejected for operational complexity; revisit only if a regulated customer requires it.
  • Customer-hosted ingest collector. Possible in principle but requires per-customer encryption keys, which we explicitly chose against.
  • In-binary crash reporting. The client_state signal carries self_check_passed (a boolean), nothing more. If the binary crashes hard, PointFive sees only the absence of subsequent client_state records — that’s by design.

  • Enrollment manifest — Plain JSON file generated by the PointFive portal, deployed via MDM. Carries tenant_id, bearer_token, user_id_hmac_key, and install-time config. Four top-level fields, no signature, no expiration.
  • Hybrid encryption — Per-record encryption that combines a symmetric cipher for the payload with an asymmetric exchange for the per-record symmetric key.
  • MDM — Mobile Device Management / endpoint management system. Examples: Jamf, JumpCloud, Intune, Ansible, Salt, Chef.
  • Pinned public key — PointFive’s ingest public key, compiled into the binary at build time. Not in the manifest. Rotation requires a binary release.
  • Tenant — A single customer organization. Has one tenant_id and one current enrollment manifest.