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) |
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 for full details.Secrets management
Environment variables
Environment variables for MCP servers are encrypted with KMS and stored in Spanner. At deployment time:- Encrypted values are loaded from the database and decrypted via KMS
- Decrypted values are injected as container environment variables in the Knative pod spec
- Values are never written to disk inside the container
Reserved prefixes
TheGUMSTACK_* 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 |
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 useshmac.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
GumstackServerhas anorganization_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_idfilter, 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:
- Before execution: The call is registered with the backend (status:
invoked). If registration fails, the tool does not execute - After execution: The call is updated with the final status, outputs, and latency
Activity logs are visible in three places: the global Activity dashboard, the server’s Activity tab, and the User detail page.
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
Comprehensive audit logging is available on the Enterprise plan.
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
Security Overview
High-level security posture
Infrastructure Security
Hosting, isolation, and network architecture
Activity & Analytics
Monitor tool usage and analytics
Permission Groups
Configure RBAC and tool access
