> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pylonsync.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Admin token rotation

> Rotate PYLON_ADMIN_TOKEN without downtime, or in an emergency after suspected compromise.

`PYLON_ADMIN_TOKEN` authenticates every privileged route:

* `/api/auth/session` POST in non-dev
* `/api/auth/upgrade` in non-dev
* `/api/admin/users/:id/export` (GDPR export)
* `/api/admin/users/:id/purge` (GDPR delete)
* `/api/sync/push` from admin contexts
* Jobs / workflows / scheduler control planes
* `/studio` in non-dev

Treat it like an SSH key: minimum 32 bytes of randomness, never in git, rotate on any suspicion of compromise.

## Without downtime (two-token rotation)

The server only reads `PYLON_ADMIN_TOKEN` at startup. Rotation requires a restart. To do it without dropping traffic:

1. **Prepare**. Generate the new token:

   ```sh theme={null}
   openssl rand -hex 32 > /etc/pylon/admin_token.new
   ```

2. **Deploy side-by-side**. Start a new instance with the new token, let the load balancer health-check promote it, stop routing traffic to the old one, and let your load balancer's connection drain finish so mid-request admin calls complete before you `SIGTERM` it. (SIGTERM itself is not graceful — see [Deploy → Shutdown and rolling deploys](/operations/deploy#shutdown-and-rolling-deploys) — so the drain has to happen at the load balancer.)

3. **Update clients**. Any automation (CI, runbooks, cron, admin UIs) that hardcodes the old token must update. Grep for the old token prefix in Vault, 1Password, GitHub Actions secrets, Cloudflare environment, etc. before deleting the value.

4. **Verify + scrub**. Hit one admin endpoint with the new token; if it works, delete the old one from your secret store.

## Emergency (suspected compromise)

1. **Generate a new token** — skip no-downtime, it's not worth the risk:

   ```sh theme={null}
   openssl rand -hex 32 > /etc/pylon/admin_token.new
   ```

2. **Revoke every active session and force re-login.** There is no global
   session-purge endpoint — invalidate all sessions by clearing the session
   DB and restarting. Every user signs back in on their next request (sessions
   are recoverable; only the login is lost):

   ```sh theme={null}
   systemctl stop pylon
   rm /var/lib/pylon/sessions.db*
   systemctl start pylon
   ```

3. **Rotate OAuth secrets too** — same blast radius if the admin account was used to configure them.

4. **Audit `audit_log`** for the period the old token was valid. The [`audit_log` plugin](/plugins/integrations) records who did what and when.

5. **File an incident report** per [`SECURITY.md`](https://github.com/pylonsync/pylon/blob/main/SECURITY.md).

## On Pylon Cloud

Admin tokens are scoped per-workspace and managed via the dashboard. Rotation is a one-click operation with no downtime — Cloud handles the side-by-side restart for you. Old tokens stop working immediately on rotation.

## What NOT to do

* **Don't use the admin token as a session token.** Admin is not "a user", it's a break-glass credential.
* **Don't commit the token to git, even in a test fixture.** The pre-commit hook rejects 32+ hex strings in tracked files.
* **Don't pass it as a URL query parameter.** `Authorization: Bearer` only. URL params leak into proxy logs and browser history.
* **Don't reuse the token across environments.** Staging ≠ prod.

## Rotation cadence

| Risk profile                      | Rotation frequency           |
| --------------------------------- | ---------------------------- |
| Low-traffic side project          | Yearly                       |
| Production user-facing app        | Quarterly                    |
| Compliance-required (SOC2, HIPAA) | Per your control framework   |
| Suspected compromise              | Immediately (emergency path) |

Set a calendar reminder. Add it to your runbook.
