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

# Data Protection

> Encryption, secrets management, credential handling, logging, and audit trails

Gumstack encrypts all sensitive data at rest and in transit, isolates credentials per-user, and provides comprehensive logging for compliance and monitoring.

## Encryption at rest

All sensitive values are encrypted using **Google Cloud KMS** (AES-256-GCM) before being written to the database.

| Data type                 | Storage              | Encryption                                 |
| ------------------------- | -------------------- | ------------------------------------------ |
| **Environment variables** | Google Cloud Spanner | KMS-encrypted                              |
| **OAuth tokens**          | Google Cloud Spanner | KMS-encrypted                              |
| **API key credentials**   | Google Cloud Spanner | KMS-encrypted                              |
| **User secrets**          | Google Cloud Spanner | KMS-encrypted                              |
| **Refresh tokens**        | Google Cloud Spanner | SHA-256 hashed (never stored in plaintext) |

The encryption key is managed entirely within Google Cloud KMS. The key never leaves Google's infrastructure.

**Encryption flow**: Plaintext → KMS `encrypt()` → Base64-encode ciphertext → store encrypted blob in Spanner.

## Encryption in transit

All external traffic uses **TLS 1.2+** with Google-managed certificates. HTTP requests are automatically redirected to HTTPS. See [Infrastructure Security](/security/infrastructure) for full details.

## Secrets management

### Environment variables

Environment variables for MCP servers are encrypted with KMS and stored in Spanner. At deployment time:

1. Encrypted values are loaded from the database and decrypted via KMS
2. Decrypted values are injected as container environment variables in the Knative pod spec
3. Values are **never written to disk** inside the container

Updating an environment variable requires a **new deployment** to take effect, since values are baked into the pod spec at deploy time.

### Reserved prefixes

The `GUMSTACK_*` and `GUMLOOP_*` prefixes are reserved for system-injected variables. User-supplied keys with these prefixes are rejected at the API level and filtered again at deployment time (defense-in-depth).

System-injected variables include:

| Variable               | Purpose                         |
| ---------------------- | ------------------------------- |
| `GUMSTACK_SERVER_ID`   | Server's unique identifier      |
| `GUMSTACK_BACKEND_URL` | Gumstack backend API URL        |
| `GUMSTACK_MCP_URL`     | Server's public MCP endpoint    |
| `GUMLOOP_BACKEND_URL`  | Gumloop backend URL (for OAuth) |

### Secret redaction

Secrets are actively redacted across multiple surfaces:

* **Runtime logs**: Bearer tokens, Authorization headers, API keys (matching `sk-`, `pk-`, `api-`, `key-` patterns), and email addresses are redacted before being returned to the UI
* **Error messages**: Secret values that appear in tracebacks or error output are replaced with `****SECRET_REDACTED****`
* **API responses**: Encrypted credential values are never included in API responses. Environment variable values are excluded from server detail responses. Only keys (names) are visible
* **Build logs**: GitHub tokens are redacted from build error output

## Credential isolation

Credentials are isolated at multiple levels:

| Scope                | How it's enforced                                                                                          |
| -------------------- | ---------------------------------------------------------------------------------------------------------- |
| **Per-user**         | All credential queries include `user_id` in the WHERE clause. One user cannot access another's credentials |
| **Per-server**       | Gumstack credentials are scoped by `user_id + server_id`                                                   |
| **Per-organization** | Gumstack servers are scoped by `organization_id`. Admin/gumstack\_admin role is required for management    |
| **Per-project**      | Variables can be owned by `project_id` with project membership verification                                |

API responses use `to_dict_safe()` methods that exclude encrypted values entirely.

## Authentication security

### OAuth 2.0

| Property                   | Value                                                 |
| -------------------------- | ----------------------------------------------------- |
| **Grant types**            | `authorization_code` and `refresh_token` only         |
| **PKCE**                   | Required (S256 method)                                |
| **Authorization code TTL** | 5 minutes                                             |
| **Access token TTL**       | 1 hour                                                |
| **Refresh token TTL**      | 6 months                                              |
| **Token format**           | JWT (`at+jwt`) signed with RS256                      |
| **Signing key**            | RSA private key stored in Google Secret Manager       |
| **Refresh token storage**  | SHA-256 hashed in Spanner (never stored in plaintext) |
| **Refresh token rotation** | Old token is revoked on every use, new token issued   |

### OAuth state protection

OAuth state parameters are signed with **HMAC-SHA256** using a key stored in Google Secret Manager, with a 10-minute expiry. Verification uses `hmac.compare_digest()` for timing-safe comparison.

### Dynamic client registration

MCP clients (Cursor, Claude, etc.) can register dynamically via the `/oauth/register` endpoint per RFC 7591. Only `authorization_code` and `refresh_token` grants are accepted.

## Organization isolation

Every API endpoint enforces organization boundaries:

* **Server operations**: Every `GumstackServer` has an `organization_id`. Requests from a different org return 403
* **Credential access**: Credential queries always include the authenticated user's org context
* **Tool access control**: Tool access ACLs are scoped per server per organization per permission group
* **Analytics queries**: All activity queries include an `organization_id` filter, preventing cross-org data access
* **Audit logs**: Only organization admins can access audit logs

## Activity logging

Every tool call made through a Gumstack server is logged with the calling user, target server, tool name, full input/output payloads, latency, status (`success`, `error`, `permission_denied`, or `timeout`), and a unique call identifier.

Logging uses a **two-phase system**:

1. **Before execution**: The call is registered with the backend (status: `invoked`). If registration fails, the tool does not execute
2. **After execution**: The call is updated with the final status, outputs, and latency

This means even failed or timed-out calls are captured.

<Info>
  Activity logs are visible in three places: the global [Activity dashboard](/platform/activity), the server's Activity tab, and the [User detail](/platform/users) page.
</Info>

## Audit logging

Beyond tool call activity, Gumstack tracks **50+ administrative event types** including:

* **Authentication**: Sign-in events
* **Credentials**: Retrieval, creation, modification, deletion
* **Organizations**: Member management, metadata changes
* **Permission groups**: Creation, member changes, deletion, default group changes
* **ACLs**: Tool access creation, updates, deletion
* **Servers**: Creation, modification, deployment events
* **Variables**: Creation, modification, deletion
* **SCIM**: Sync operations, user provisioning/deprovisioning

Each audit event captures: event ID, timestamp, event type, user ID, details (JSON), source IP, and user agent.

<Note>
  Comprehensive audit logging is available on the **Enterprise** plan.
</Note>

## Error handling

Error responses use structured formats (`error` and `error_description` fields) without exposing stack traces, internal URLs, or database details. In production, uncaught errors return generic messages to prevent information disclosure.

## Related docs

<CardGroup cols={2}>
  <Card title="Security Overview" icon="shield" href="/security/overview">
    High-level security posture
  </Card>

  <Card title="Infrastructure Security" icon="server" href="/security/infrastructure">
    Hosting, isolation, and network architecture
  </Card>

  <Card title="Activity & Analytics" icon="chart-column" href="/platform/activity">
    Monitor tool usage and analytics
  </Card>

  <Card title="Permission Groups" icon="users" href="/platform/permission-groups">
    Configure RBAC and tool access
  </Card>
</CardGroup>
