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

# Credential Management

> How API credentials are stored, encrypted, and managed in Radarboard.

# Credential Management

Radarboard stores API credentials in an encrypted database instead of `.env` files. This enables credential management through the settings UI without server restarts.

## How it works

1. **Encryption**: All credentials are encrypted at rest using AES-256-GCM
2. **Storage**: Encrypted data stored in the `widget_credentials` table
3. **Master key**: A single `ENCRYPTION_KEY` environment variable provides the encryption key
4. **Per-service**: Each external service has its own credential record (e.g., `sentry`, `vercel`, `linear`)

## Setting up

Add an `ENCRYPTION_KEY` to your `.env`:

```bash theme={null}
# Generate a random 32-byte hex key
openssl rand -hex 32
```

```env theme={null}
ENCRYPTION_KEY=your_64_character_hex_key_here
```

<Warning>
  This is the only secret that must remain in `.env`. All other API credentials can be managed through the settings UI.
</Warning>

## Credential types

### API Key (manual entry)

For services like RevenueCat, Sentry, and BetterStack. Enter the token directly in the Integrations settings tab.

### OAuth (browser redirect)

For services like GitHub and Google Search Console. The flow:

1. Enter your OAuth app's Client ID and Client Secret
2. Click "Connect with {service}"
3. Authorize on the provider's website
4. Tokens are stored automatically

## For Google: `gws` CLI shortcut

Instead of creating a Google Cloud OAuth app manually, you can use the [Google Workspace CLI](https://github.com/googleworkspace/cli):

```bash theme={null}
npx @googleworkspace/cli auth setup
npx @googleworkspace/cli auth login -s webmasters
```

Then click "Import from gws CLI" in the Google Search Console integration settings.

## Adding credentials to a new widget

When creating a new widget, declare its auth requirements in the descriptor:

```typescript theme={null}
export const myWidgetDescriptor: WidgetDescriptor = {
  // ...other fields
  auth: {
    id: "my-service",
    name: "My Service",
    type: "api_key",
    fields: [
      { key: "apiKey", label: "API Key", type: "password" },
    ],
    testEndpoint: "/api/credentials/test",
    docsUrl: "https://my-service.com/docs/api",
  },
};
```

For OAuth providers:

```typescript theme={null}
auth: {
  id: "my-oauth-service",
  name: "My OAuth Service",
  type: "oauth",
  fields: [
    { key: "clientId", label: "Client ID", type: "text" },
    { key: "clientSecret", label: "Client Secret", type: "password" },
  ],
  docsUrl: "https://my-service.com/oauth/apps",
  oauth: {
    provider: "my-provider",  // must match a key in oauth-providers.ts
    scopes: ["read", "write"],
    setupInstructions: "Create an OAuth app at ...",
  },
},
```

Then add the provider to `apps/app/lib/oauth-providers.ts`.

## 1Password CLI Backup (optional)

If you use 1Password, you can backup and restore credentials via the `op` CLI:

```bash theme={null}
# Install 1Password CLI
brew install 1password-cli

# Sign in
op signin
```

Then in the app:

* **Export to 1Password**: `POST /api/credentials/1password` with `{ action: "export", vault: "Dev" }`
* **Import from 1Password**: `POST /api/credentials/1password` with `{ action: "import", vault: "Dev" }`

Credentials are stored as 1Password items with the title prefix "Radarboard - {service}".

<Note>
  The 1Password CLI must be installed and authenticated on the server. This only works for local/self-hosted deployments.
</Note>

## Security

* Credentials encrypted with AES-256-GCM (random IV per encryption)
* Master key stored as environment variable (not in database)
* OAuth state parameter prevents CSRF attacks
* Cookies are `httpOnly`, `secure` (production), `sameSite: lax`
* Tokens never appear in URLs or browser history
