Summary
The current integrations implementation stores OAuth access and refresh tokens directly in the Integration table.
This is acceptable for the current local/self-hosted development setup, where the app is private and the database is controlled by the same operator. Before Princeps is offered as a serious multi-user production service, integration tokens should be encrypted at rest.
Current behavior
Connected providers such as Google Calendar and Google Drive save tokens in:
Integration.accessToken
Integration.refreshToken
These tokens are only used server-side and are never returned to the client.
Intended production behavior
Before saving provider tokens:
- Encrypt access and refresh tokens with a server-side secret key.
- Store only encrypted values in the database.
- Decrypt tokens server-side only when calling provider APIs.
- Keep the encryption key in environment/secret storage, not in the database.
Scope
This is a production-hardening task, not an urgent local-development blocker.
The current behavior is intentional for local hosting and early development simplicity. The change should be made before multi-user hosted production.
Suggested implementation
- Add a small server-only token encryption helper, e.g.
lib/integrations/shared/crypto.ts.
- Use AES-GCM or another authenticated encryption mode from Node
crypto.
- Add an env var such as
INTEGRATION_TOKEN_ENCRYPTION_KEY.
- Encrypt tokens in
upsertIntegration().
- Decrypt tokens in
getValidToken().
- Support a migration/backward-compatible read path if existing plaintext local tokens should keep working.
- Document setup in
docs/04_DEVELOPER_PLAYBOOK.md and .env.example.
Acceptance criteria
- Integration tokens are encrypted before being stored.
- Provider calls still work after token refresh.
- Existing local/dev behavior is documented.
- Missing encryption key fails clearly in production.
- No token values are ever sent to the client or logs.
Summary
The current integrations implementation stores OAuth access and refresh tokens directly in the
Integrationtable.This is acceptable for the current local/self-hosted development setup, where the app is private and the database is controlled by the same operator. Before Princeps is offered as a serious multi-user production service, integration tokens should be encrypted at rest.
Current behavior
Connected providers such as Google Calendar and Google Drive save tokens in:
Integration.accessTokenIntegration.refreshTokenThese tokens are only used server-side and are never returned to the client.
Intended production behavior
Before saving provider tokens:
Scope
This is a production-hardening task, not an urgent local-development blocker.
The current behavior is intentional for local hosting and early development simplicity. The change should be made before multi-user hosted production.
Suggested implementation
lib/integrations/shared/crypto.ts.crypto.INTEGRATION_TOKEN_ENCRYPTION_KEY.upsertIntegration().getValidToken().docs/04_DEVELOPER_PLAYBOOK.mdand.env.example.Acceptance criteria