Extension Secret Store¶
Kiket extensions often need API keys, signing secrets, or credentials for downstream systems. The Secret Store gives admins a consistent way to supply those secrets, keep them scoped to the right projects, and share them with self-hosted extensions through the SDKs.
Key concepts¶
| Concept | Description |
|---|---|
| Scopes | Secrets can belong to an organization, a single project, a specific extension installation, a marketplace product installation, or the extension itself (partner defaults). Scopes isolate configuration so multiple installs of the same extension never share credentials unless you choose to. |
| Keys | Uppercase snake-case identifiers (e.g., MAILCHIMP_API_KEY). Keys are unique within their scope. |
| Values | Encrypted secret material managed by Kiket. You can also reference an external secret (Google Secret Manager or Vault) instead of storing the value directly. |
| Rotation metadata | Each secret tracks the last rotation timestamp and a rotation counter for compliance exports. |
Scope hierarchy¶
Secrets follow a fallback resolution order when accessed at runtime:
- Extension Installation (project-level) – Most specific; secrets set during the project wizard for a specific project
- Product Installation (organization-level) – Shared across all projects from marketplace installs
- Extension (org-wide for this extension) – Shared across all projects using this extension; configured via the org-level wizard
- Project – Legacy project-scope secrets
- Organization – Organization-wide defaults
When resolving a secret key, Kiket checks each scope in order and returns the first match. This allows organizations to set shared credentials while still permitting project-level overrides.
Extension vs Installation scope¶
Extension manifests can declare secrets with different scopes:
# Example manifest configuration
extension:
configuration:
# Organization-wide credential (scope: extension)
ZOOM_CLIENT_ID:
type: string
secret: true
required: true
scope: extension # Shared across all projects
label: Zoom Client ID
# Project-specific setting (scope: installation, default)
CHANNEL_ID:
type: string
required: true
scope: installation # Per-project override
label: Default Channel
scope: extension– Credentials configured once at the organization level and shared across all projects using the extension. Example: OAuth client ID/secret for Zoom.scope: installation(default) – Credentials configured per-project during the project setup wizard. Example: Slack channel ID for notifications.
When installing an extension that has scope: extension fields:
1. You'll first see the org-level wizard to configure organization-wide credentials
2. When adding to a project, you'll only see project-specific fields
3. Project wizards show "Using organization-wide credentials" with an option to override
Admin workflow¶
-
Review requirements
Marketplace listings and extension manifests declare required secrets. During install you’ll see a checklist of missing secrets. -
Create secrets Navigate to Settings → Organization Extension Secrets:
- Choose a scope (organization/project/extension/product install).
- Select the extension + key you want to configure (autocomplete suggests keys from the manifest).
- Enter a value or select “Link external secret” and provide the GSM/Vault path.
-
Save to encrypt the value and record an audit log entry.
-
Rotate secrets
Click Rotate next to any key or runkiket extensions secrets set --key FOO --value .... The UI prompts for a new value and automatically bumps the rotation count. Extensions subscribe to rotation webhooks and reload credentials automatically. -
Verify completion
The installation overview page and CLIkiket marketplace statuscall out missing or placeholder (scaffolded) secrets. Extensions cannot be enabled until all required keys are satisfied.
CLI & API¶
All operations are available via REST (/api/v1/extensions/secrets) and the CLI:
# List secrets for a scope
kiket extensions secrets list \
--scope extension \
--id com.example.mailchimp
# Create/update from an env file
kiket extensions secrets set \
--scope extension \
--id com.example.mailchimp \
--key MAILCHIMP_API_KEY \
--env-file .env.mailchimp
# Link to an external secret backend
kiket extensions secrets link \
--scope project \
--id PROJECT_KEY \
--key CRM_WEBHOOK_SECRET \
--external secrets/kiket/projects/project_key/crm_webhook
The API never returns plaintext values. Responses include masked_value_present, rotation info, and whether the secret is sourced from an external backend.
Runtime delivery¶
- Every webhook payload (
ExtensionInvocation) now includes asecretsobject populated just before delivery. Values come from the Secret Store using the scope hierarchy (extension installation → product installation → extension → project → organization) and are never persisted alongside the invocation record. - Secrets flagged as
secret: truein the manifest configuration automatically flow into the payload; you can also list explicitsecret_keysunderdelivery.callbackto request additional keys. - If you enable mutual TLS, declare
callback.mtls.certificate_key/private_key_key. Those secrets are fetched for the TLS handshake only—they are excluded from the payload. - Missing keys show up under
secret_status.missingin the payload so extensions can fail gracefully while admins finish configuration.
SDK access¶
All SDKs provide a secret helper in the handler context that unifies payload secrets with environment variable fallbacks:
Secret Helper (Recommended)¶
The secret helper resolves secrets in order: payload secrets (per-org/project) → environment variables (extension defaults).
# Ruby SDK
def handle_event(payload, context)
smtp_host = context[:secret].call("SMTP_HOST")
smtp_user = context[:secret].call("SMTP_USERNAME")
# Returns payload["secrets"]["SMTP_HOST"] || ENV["SMTP_HOST"]
end
# Python SDK
def handle_event(payload, context):
smtp_host = context['secret']('SMTP_HOST')
smtp_user = context['secret']('SMTP_USERNAME')
// Node.js SDK
async function handleEvent(payload: KiketEvent, context: Context) {
const smtpHost = context.secret('SMTP_HOST');
const smtpUser = context.secret('SMTP_USERNAME');
}
// Java SDK
public ExtensionResponse handleEvent(KiketEvent event, Context context) {
String smtpHost = context.secret("SMTP_HOST");
String smtpUser = context.secret("SMTP_USERNAME");
}
// .NET SDK
public ExtensionResponse HandleEvent(KiketEvent evt, Context context)
{
var smtpHost = context.Secret("SMTP_HOST");
var smtpUser = context.Secret("SMTP_USERNAME");
}
// Go SDK
func handleEvent(event kiket.Event, context kiket.Context) kiket.Response {
smtpHost := context.Secret("SMTP_HOST")
smtpUser := context.Secret("SMTP_USERNAME")
}
Why use the secret helper?¶
The secret helper enables multi-tenant behavior:
- Per-organization secrets: When admins configure credentials in the setup wizard, those values are bundled into
payload["secrets"]at delivery time - Fallback to defaults: First-party extensions can set default credentials via Cloud Run environment variables
- No code changes needed: Extensions automatically pick up org-specific overrides without modification
Legacy access methods¶
The following methods are still supported but the secret helper is recommended:
- Python SDK –
from kiket.runtime import SecretStorethenSecretStore().get("MAILCHIMP_API_KEY") - TypeScript SDK –
context.secrets.get("MAILCHIMP_API_KEY") - CLI tooling –
kiket marketplace installaccepts--env-fileto pre-populate secrets
Compliance & auditing¶
- Every secret change writes an audit log entry (who, when, scope, key, whether external storage is used).
Extensions::SecretAuditJob(described in the admin settings page) raises alerts for secrets older than your rotation policy.- You can export a rotation report per organization for SOC2/HIPAA evidence.
Monitoring¶
The Secret Store includes built-in monitoring for access patterns, rotation compliance, and anomaly detection:
- Access frequency tracking: Alerts fire when unusual read patterns occur
- Rotation compliance: Scheduled jobs flag secrets exceeding your rotation policy
- Audit trail: Every operation is logged for compliance reporting
Access logs & anomaly alerts¶
- Visit Settings → Extensions → Access Logs (or run the
View secret access logscommand in the command palette) to review the last 200 secret reads/writes. Filters let you pivot by key, action, or source. - Programmatic consumers can hit
GET /api/v1/secrets/auditwith an organization admin session to export the same data. - Kiket automatically monitors access frequency. If more than 25 reads occur against the same key within a 15-minute window, all organization admins receive a high-priority notification (and the CLI can poll the same endpoint to trigger automation).
Frequently asked questions¶
Can I use my own vault?
Yes. Set external_secret_name to the GSM or Vault path. Kiket stores only the reference and fetches the value at runtime using the tenant’s service account.
Can multiple installs reuse the same key?
Each install can have a key with the same name, but the values are isolated thanks to scope scoping. Organization-level secrets act as defaults if a project or extension key is missing.
What happens if a required secret is missing?
Extension invocations fail fast, Marketplace surfaces the missing key, and the CLI exits with a non-zero status so you can catch misconfigurations in automation.