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.”
1. Why no control plane
Section titled “1. Why no control plane”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).
2. Distribution channels
Section titled “2. Distribution channels”| Platform | Primary channel | MDM integration |
|---|---|---|
| macOS | Homebrew 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 |
| Windows | MSI installer | Group Policy, Intune, Microsoft Configuration Manager |
| Direct download | GitHub Releases (signed archives + checksums) | Manual or scripted install |
Each release produces cross-compiled artifacts for linux/darwin/windows × amd64/arm64, plus:
- Signed
.pkgfor macOS (Apple Developer ID + notarization). - Signed MSI for Windows (EV Code Signing cert).
.deband.rpmpackages for the Linux repos.- A signed
release-manifest.jsonlisting checksums and version metadata.
3. Code signing
Section titled “3. Code signing”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.
4. Enrollment
Section titled “4. Enrollment”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.
4.1 What the customer admin does
Section titled “4.1 What the customer admin does”-
Logs into the PointFive customer portal via the customer’s SSO.
-
Confirms
tenant_id(allocated when the customer onboarded). -
Picks install-time configuration from a form: tier cap, project hash mode, recovery cache enable, sample allowlist, sample rate, retention extensions.
-
Clicks Generate manifest.
-
Portal generates a fresh
bearer_tokenand a fresh per-tenantuser_id_hmac_key(32 bytes from the portal’s CSPRNG). -
Portal returns the manifest as
enrollment.json. -
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.
- macOS / Linux:
-
(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 enrolleven on a managed endpoint with an MDM-deployed system manifest — the per-user file will shadow it andtokenshift doctorwill warn about the override. -
-
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.
4.1.1 tokenshift enroll
Section titled “4.1.1 tokenshift enroll”For self-serve installs (and dev machines), the CLI provides:
tokenshift enroll <path> # copy a file into ~/.tokenshift/enrollment.jsontokenshift enroll - # read stdin (paste / pipe)tokenshift enroll --dry-run … # validate without writingtokenshift 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.
4.2 What the binary does on first run
Section titled “4.2 What the binary does on first run”- Reads the enrollment manifest by searching, in order:
$TOKENSHIFT_ENROLLMENT_PATH(dev/test override), then~/.tokenshift/enrollment.json(per-user — written bytokenshift enroll), then the system path (/etc/tokenshift/enrollment.jsonor%PROGRAMDATA%\tokenshift\enrollment.json— deployed by MDM). - Parses it. If absent or malformed → fail-closed (logs error, hooks exit 0 so Claude Code is unaffected).
- Generates a random
client_idUUID and writes it to/var/lib/tokenshift/client.json(machine-level, persists across upgrades and re-enrollment). - Resolves the local user’s email via the fallback chain (see §4.3).
- Computes
user_id = HMAC(manifest.user_id_hmac_key, email)locally. If no email source is available,user_idis null and emitted records simply omit the field. - Caches
tenant_id,bearer_token,user_id_hmac_key, andconfigin memory. - Emits its first record: a
client_stateheartbeat. 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).
4.3 User-email resolution
Section titled “4.3 User-email resolution”Run silently in this order; first hit wins:
| Order | Source | Notes |
|---|---|---|
| 1 | git config --global user.email | Primary. Set on essentially every developer machine — git refuses to commit without it. |
| 2 | $TOKENSHIFT_USER_EMAIL env var | Escape hatch the customer admin can populate via MDM if needed. |
| 3 | None | user_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.
4.4 Manifest contents
Section titled “4.4 Manifest contents”{ "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.
4.5 When the manifest changes
Section titled “4.5 When the manifest changes”Three reasons it might change in steady state:
- Config change — admin wants to raise the tier cap, enable Tier 4 sampling on a specific rule, etc. Regenerate from portal, redeploy via MDM.
- 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.
- 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 olduser_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. Encryption
Section titled “5. Encryption”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.
5.2 The scheme
Section titled “5.2 The scheme”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.
5.4 What this trades off
Section titled “5.4 What this trades off”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.
6. Ingest pipeline
Section titled “6. Ingest pipeline”End-to-end:
Client PointFive ingest────── ────────────────encrypt(record) → HTTPS POST → stateless receiver └─ decrypt └─ allowlist-check └─ persist (one signal type → one stream) └─ 200 OKKey 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.
7. Updates
Section titled “7. Updates”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_statesignal (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.
8. Version skew
Section titled “8. Version skew”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_staterows and (when records are being rejected) via the absence of recenttool_invocationrows for affectedclient_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.
9. Threat model
Section titled “9. Threat model”| Threat | Mitigation |
|---|---|
| Malicious tampered binary substituted for the legitimate one | Code signing on every platform; MDM enforces signature verification before install. |
| Malicious enrollment manifest substituted | Maximum 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 exfiltrated | Attacker 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 MITM | TLS 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 breach | Per-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 breach | Worst 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 compromise | Attacker 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 code | client_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.email | user_id is null on emissions; analytics degrade gracefully to per-machine. No security impact. |
Attacker tries to reverse user_id from leaked PointFive data | Without 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_statesignal carriesself_check_passed(a boolean), nothing more. If the binary crashes hard, PointFive sees only the absence of subsequentclient_staterecords — that’s by design.
Glossary
Section titled “Glossary”- 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_idand one current enrollment manifest.