Skip to content

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:

  1. Extension Installation (project-level) – Most specific; secrets set during the project wizard for a specific project
  2. Product Installation (organization-level) – Shared across all projects from marketplace installs
  3. Extension (org-wide for this extension) – Shared across all projects using this extension; configured via the org-level wizard
  4. Project – Legacy project-scope secrets
  5. 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

  1. Review requirements
    Marketplace listings and extension manifests declare required secrets. During install you’ll see a checklist of missing secrets.

  2. Create secrets Navigate to Settings → Organization Extension Secrets:

  3. Choose a scope (organization/project/extension/product install).
  4. Select the extension + key you want to configure (autocomplete suggests keys from the manifest).
  5. Enter a value or select “Link external secret” and provide the GSM/Vault path.
  6. Save to encrypt the value and record an audit log entry.

  7. Rotate secrets
    Click Rotate next to any key or run kiket 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.

  8. Verify completion
    The installation overview page and CLI kiket marketplace status call 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 a secrets object 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: true in the manifest configuration automatically flow into the payload; you can also list explicit secret_keys under delivery.callback to 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.missing in 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:

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:

  1. Per-organization secrets: When admins configure credentials in the setup wizard, those values are bundled into payload["secrets"] at delivery time
  2. Fallback to defaults: First-party extensions can set default credentials via Cloud Run environment variables
  3. 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 SDKfrom kiket.runtime import SecretStore then SecretStore().get("MAILCHIMP_API_KEY")
  • TypeScript SDKcontext.secrets.get("MAILCHIMP_API_KEY")
  • CLI toolingkiket marketplace install accepts --env-file to 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 logs command 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/audit with 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.