# Security

> How to keep credentials safe and what Coline does to limit access.

Source: https://docs.coline.app/authentication/security

## Keeping credentials safe

* **Store keys as secrets.** Put API keys and OAuth client secrets in your environment or secret manager. Never commit them, ship them in a browser or mobile bundle, or put them in an app built with the SDK.
* **One key per service.** Separate keys can be revoked separately and have separate rate limits.
* **Ask for the fewest scopes.** A sync job that only reads tasks needs `taskboards.read`, nothing else.
* **Rotate after exposure.** If a key may have leaked, create a replacement, deploy it, and revoke the old one in the console. Revocation takes effect immediately.
* **Use PKCE and check `state`.** Every OAuth authorization needs an S256 code challenge. Check that `state` matches what you sent before exchanging a code.
* **Store refresh tokens like passwords.** Each refresh issues a new refresh token and retires the old one.

## What Coline enforces

* **Scopes are a ceiling, not a grant.** An OAuth token can only reach what its person can already open in Coline, narrowed further by its scopes. An API key can only reach its own workspace.
* **Secrets are stored hashed.** API keys and OAuth client secrets are shown once and stored as hashes.
* **Writes are audited.** Writes made with an API key are recorded in the workspace audit log against the key.
* **Workspace state is respected.** Archived workspaces are read-only, and suspended workspaces refuse API requests.
* **AI clients don't send email on their own.** Mail and external calendar invites created through MCP or the API wait for the person to approve them, unless they've turned that off for trusted clients.
* **Apps run with a ceiling.** Apps built with the SDK can only use the permissions a workspace admin approved, can't exceed the effect each tool declares, and never see stored secrets or connected-account tokens; Coline injects them into requests to the hosts the app declared.
* **Browser writes need the same origin.** Requests that change data without a bearer token must come from a same-origin Coline session (`MUTATION_ORIGIN_REQUIRED` otherwise).

## Reporting a vulnerability

Email [support@coline.app](mailto:support@coline.app) with "Security" in the subject. Please don't test against other people's workspaces.
