# Machine credentials — BrandApp Playground

> Server-to-server auth without a shared secret — private_key_jwt assertions, DPoP-bound tokens, a published JWKS, and a dedicated adapter client.

- Canonical page: https://brandapp-playground.vercel.app/auth/machine
- Markdown: https://brandapp-playground.vercel.app/auth/machine.md
- SDK module: `@reopt-ai/brandapp-sdk/machine`

## What it demonstrates

- `reopt-keygen` — generates an EdDSA key; the private JWK lives only in
  `REOPT_CLIENT_PRIVATE_KEY_JWK`, the public half is served at
  `/.well-known/reopt-client-jwks.json` with `buildClientJwks()`.
- `createMachineClient()` — signs a `private_key_jwt` assertion (RFC 7523) for the
  token endpoint and binds every request with a DPoP proof (RFC 9449).
- Two Reopt OAuth clients share the key: `adapter` (`brandapp:auth-adapter` only, used
  by `createReoptAdapter`) and `machine` (`brandapp:read` + `brandapp:write`, used by
  every other SDK factory through `authorization: machine.headers`).

## Why

A login client secret used to open every machine surface, including the remote auth
adapter that stores user sessions. With RFC-0034 the login client keeps only the
authorization-code flow; machine calls carry a token that names its audience and is
useless without the key that minted its proof.

## Zero-downtime switch

Without a key the playground keeps using `BRANDAPP_CLIENT_SECRET`. Deploy the code,
register the key, confirm `/api/auth/machine?probe=1` reports both tokens, then let
Reopt close the login client's transitional machine scopes.

## Rotation

Set `REOPT_CLIENT_PRIVATE_KEY_JWK_NEXT`: the JWKS lists both keys and signing moves
to the new one. After Reopt picks it up (≤ 5 minutes) move NEXT into the primary
variable and drop NEXT.

## Related

- [Authentication](https://brandapp-playground.vercel.app/auth.md)
- [EAV Overview](https://brandapp-playground.vercel.app/eav.md)
- [Health Check](https://brandapp-playground.vercel.app/health.md)

---

Index of every document: https://brandapp-playground.vercel.app/llms.txt
Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt
HTTP API description: https://brandapp-playground.vercel.app/openapi.json
