From hush-uam
Author Kubernetes manifests for Hush UAM (Universal Access Management) — AccessCredential, AccessPrivilege, AccessPolicy CRDs from Hush Security. Use this skill whenever the user asks about Hush, hush-am, hush-uam, Hush policies/credentials/privileges, the `am.hush.security` API group, or wants to create/generate/scaffold k8s YAML for any access credential, access privilege, or access policy in a Hush deployment.
How this skill is triggered — by the user, by Claude, or both
Slash command
/hush-uam:hush-uam-manifestThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Generate well-formed CRD manifests for the Hush UAM (Universal Access Management) operator from Hush Security. The CRDs live under the API group `am.hush.security/v1alpha1` and cover three resources: `AccessCredential`, `AccessPrivilege`, and `AccessPolicy`. The operator forwards `spec.config` straight to the Hush API, so the keys inside `config` must match what the Hush API expects for the cho...
references/apigee.mdreferences/aws_access_key.mdreferences/aws_wif.mdreferences/azure_app.mdreferences/azure_wif.mdreferences/bedrock.mdreferences/datadog-scopes.mdreferences/datadog.mdreferences/elasticsearch.mdreferences/gcp_sa.mdreferences/gcp_wif.mdreferences/gemini.mdreferences/gitlab.mdreferences/grok.mdreferences/kv.mdreferences/mariadb.mdreferences/mongodb.mdreferences/mongodb_atlas.mdreferences/mysql.mdreferences/openai.mdGenerate well-formed CRD manifests for the Hush UAM (Universal Access Management) operator from Hush Security. The CRDs live under the API group am.hush.security/v1alpha1 and cover three resources: AccessCredential, AccessPrivilege, and AccessPolicy. The operator forwards spec.config straight to the Hush API, so the keys inside config must match what the Hush API expects for the chosen type.
Every manifest you generate MUST use metadata.namespace: hush-security. The Hush operator only watches the hush-security namespace; resources placed anywhere else will silently fail to sync with the Hush platform. This applies to:
AccessCredential, AccessPrivilege, AccessPolicy CRsSecret you generate alongsideDo not ask the user which namespace to use, do not accept overrides, and do not infer a namespace from the prompt. If the user explicitly asks for a different namespace, push back and explain that the operator only watches hush-security.
Note: this constraint is about where the CRs live. It is NOT the same as the workload's namespace, which appears in attestationCriteria entries of type k8s:ns and refers to where the workload consuming the credential runs. Those two namespaces are independent — the workload almost always lives in a different namespace than hush-security.
Every supported credential type has its own reference file at references/<type>.md documenting the credential schema, the matching privilege schema (or "no privilege type" note), the auth-principal permissions, and any type-specific notes (auth method choices, connection-string templates, etc.). You MUST read the matching references/<type>.md before emitting a manifest for that type — values are not interchangeable and inventing names will produce broken manifests.
See the Type catalog section below for the full list with one-liner descriptions and links.
For privilege types with very large enumerated value lists, additional reference docs live alongside the per-type files:
references/datadog-scopes.md — full Datadog scope list (~600 scopes)references/sendgrid-scopes.md — full SendGrid scope listreferences/twilio-permissions.md — full Twilio permission pathsreferences/redis-commands.md — full Redis command listreferences/snowflake-privileges.md — full Snowflake privilege map per resource typeThe per-type file points you to these when needed.
Walk through these with the user in this order. Skip the items that don't apply to the artifacts being generated.
Policy-first when the prompt is policy-shaped. When the user says "create a policy for X", they care about the policy — who gets access and how creds are delivered. Ask the policy plumbing first (attestation, delivery), then the credential details, then the privilege. Don't bury the policy questions behind a wall of credential field prompts; that's the opposite of what the user asked for.
Finish a resource before starting the next. This is a strict rule. When you're gathering inputs for a resource (policy, credential, or privilege), collect both the structural decisions and the specific values for that resource before moving on. Don't ask the structural question for one resource (e.g. "which attestation pattern?") in the same round as a structural question for a different resource (e.g. "which auth method for the credential?"). Worse, don't defer the values until later: if the user picks k8s:ns + k8s:sa for attestation, ask in the same round (or the very next, before touching credential details) for the workload namespace and SA name. Same for delivery — once they pick "split env vars", immediately ask which env var name maps to which credential field.
AskUserQuestion for everythingUse the AskUserQuestion tool for every input, not just multiple-choice decisions. The tool always offers an Other option that lets the user type a custom value, so it works equally well for free-form fields like metadata names, project IDs, hostnames, env var names. The user gets one consistent UI for the whole interview instead of being toggled between structured choices and "reply with numbers" prose.
Never ask about namespace — it's always hush-security (see the critical constraint above).
Rules:
AskUserQuestion call. Cap is 4 — when there are more inputs to gather, fire a second call after answers come back.(Recommended) and put it first. The tool auto-adds Other for free-form input — don't list it as one of your options.gemini-prod), present the default as the recommended option and a generic alternative (e.g. Use a different name) as the second. The user clicks through or picks Other to type a custom value.project_id, hostnames, AWS account ARNs), don't fake a default. Use options like I'll provide it now plus a contextual alternative (Use environment-specific value) — the user picks Other and types it. Don't lump these into a defaults shortcut.k8s:ns + k8s:sa, or env var name → cred field mappings for env splitted).type is genuinely pinned by phrases like "policy for postgres" — don't re-ask. Scope is not pinned by mentioning a policy: "I want a policy for X" could mean just the policy (referring to existing cred/privilege via id or name), cred+policy, or the full trio. Always ask scope when a policy is involved, even if the trio is the most likely answer; present the trio as Recommended but list the other options explicitly so the user can opt out.defaults shortcutOffer a defaults shortcut only for fields that genuinely have defaults — CR names, attestation pattern, env var name, delivery type, etc. Required-no-default values (project_id, host, port for non-standard ports, the workload's namespace and SA for attestation, etc.) must be asked individually; never silently fill them in. Namespace is fixed (hush-security) and is not part of the shortcut.
Example: "Or say defaults and I'll use: cred gemini-prod, policy gemini-app-policy, attestation by k8s:ns + k8s:sa (you tell me the workload's namespace + SA), env delivery as GEMINI_API_KEY — you still need to provide the GCP project ID."
The order below is grouped by resource. Within each resource, structural choices come first and the values they unlock follow immediately — never defer values to a later resource's round.
Scope — always ask when a policy is involved. Present these options (with the trio as Recommended for greenfield use cases):
AccessCredential + AccessPrivilege + AccessPolicy (Recommended)AccessCredential + AccessPolicy (cred is new, privilege already exists or isn't needed for this type)AccessPolicy only (cred and privilege already exist; the policy will reference them by id or name)AccessCredential only / AccessPrivilege only / AccessPolicy only)Don't auto-assume the trio. The user might already have the cred and privilege managed via Hush API/UI/Terraform and only want a policy CR to wire attestation + delivery for a specific workload. If they pick "policy only" or "cred + policy", remember to ask whether the unowned references should use name (CR in hush-security) or id (managed externally) — see "Referencing credentials and privileges".
Type — postgres, gemini, aws_access_key, etc. Skip if implied by the prompt.
Walk the user through every policy decision before touching the credential.
<type>-<purpose>-policy (e.g. pg-app-policy).k8s:ns + k8s:sa. Available types: k8s:ns, k8s:sa, k8s:pod-label (requires key), k8s:pod-name, k8s:container-name. At least one required.env (split vars) / env (connection-string template) / volume / sdk / matching WIF type. For DB-style creds, the connection-string template is usually the right default.env (split): one env var name per credential field to expose (e.g. POSTGRES_USERNAME, POSTGRES_PASSWORD).env (template): the single env var name (e.g. DATABASE_URL).volume: mount point + a relative file path per item.sdk: the secret name + one in-SDK key name per item.role_arn / pool details / subject_kind / subject as appropriate for the federation type.enabled management — toggle outside K8s (Recommended, omit spec.enabled) or reconcile in K8s (set it). See "How enabled interacts with drift correction".id/name)All credential decisions in one block — structural choices, field values, and secret strategy.
<type>-<env> style (e.g. gemini-prod, pg-prod).references/<type>.md): GCP auth method (federation vs uploaded key) for gemini/gcp_sa/apigee, service_account_bound for gemini, auth_method: password|key_pair for snowflake, engine: redis|elasticache for redis, client_id+client_secret vs public_key+private_key for mongodb_atlas, etc. Ask the structural choice and immediately follow with any extra values it requires.postgres: host, port, db_name, ssl_mode, username. Pull the per-type field list and defaults from references/<type>.md. Volunteer defaults inline ("port — default 5432") to keep the conversation tight.secretRef).
secretRef over inlining.keyMappings.keyMappings.plaintext, kv, gemini, bedrock, aws_wif, gcp_wif, azure_wif)<type>-<purpose> (e.g. pg-readonly).references/<type>.md. Offer well-known presets when the type has them (e.g. read-only vs read-write for SQL). For privileges with very large enumerated lists (datadog, sendgrid, twilio, redis commands, snowflake), confirm scope/permission/command names with the user and load the matching oversized reference file (references/<file>.md) before generating.Use one or more AskUserQuestion calls per resource. Never split a resource across rounds with another resource's questions in between. A typical trio for a policy-shaped prompt looks like:
enabled management can fit here too if there's room.)Skip rounds that don't apply (e.g. no privilege round for gemini). Combine rounds when possible (the cap is 4 questions per call) but never combine across resources. If Round 2 doesn't fit alongside Round 1's leftovers, fire Round 2 as its own call.
apiVersion: am.hush.security/v1alpha1
kind: AccessCredential
metadata:
name: <metadata-name> # decoupled from spec.name
namespace: hush-security # FIXED — never change
spec:
name: <display-name> # what the API sees
type: <type> # see "Type catalog" section
description: <optional>
config: # non-sensitive fields go here
<field>: <value>
secretRef: # only if the type has sensitive fields
name: <k8s-secret-name>
keyMappings: # see "How keyMappings behaves" below
<expected-field>: <secret-key>
keyMappings behaveskeyMappings is not just a rename map — it's also a filter. The two modes:
Without keyMappings: the operator forwards every key in the Secret to the Hush API as-is. If the Secret has any keys the API doesn't recognize for that credential type (e.g. username, created_at, rotation_id, audit metadata, an unrelated key the team stored there), the create/update call will fail. This works only when the Secret was specifically created with exactly the canonical key names and nothing else.
With keyMappings: only the keys mentioned in the mapping are forwarded. Everything else in the Secret is ignored. Each entry is <expected-field>: <secret-key> — the left side is what the Hush API expects, the right side is the actual key in the K8s Secret. Even an identity mapping (e.g. password: password) is valid and useful — it explicitly opts in to that one key and filters out the rest.
password, api_key, etc.), skip keyMappings. You control the Secret, so there are no extra keys.keyMappings, even if every entry is identity. This avoids silent failures when the Secret has extra keys, and makes the manifest self-documenting.# Existing Secret with extra keys — keyMappings filters to just what's needed
secretRef:
name: pg-creds
keyMappings:
password: password # identity map — still required to filter out extras
# Existing Secret with non-canonical keys — keyMappings remaps and filters
secretRef:
name: pg-creds
keyMappings:
password: db_pass
apiVersion: am.hush.security/v1alpha1
kind: AccessPrivilege
metadata:
name: <metadata-name>
namespace: hush-security # FIXED — never change
spec:
name: <display-name>
type: <type>
description: <optional>
config:
<type-specific fields, see catalog>
There is no secretRef on privileges.
apiVersion: am.hush.security/v1alpha1
kind: AccessPolicy
metadata:
name: <metadata-name>
namespace: hush-security # FIXED — never change. The CR lives here regardless of where the workload runs.
spec:
name: <display-name>
description: <optional>
# enabled: true # OPTIONAL — see "How `enabled` interacts with drift correction" below
accessCredentialRef: # exactly one of `name` or `id` — see "Referencing creds/privileges" below
name: <cred-cr-name>
accessPrivilegeRefs: # OMIT for types that don't take privileges (see below)
- name: <priv-cr-name> # exactly one of `name` or `id` per entry
attestationCriteria: # min 1
- type: k8s:ns # one of: k8s:ns, k8s:sa, k8s:pod-label, k8s:pod-name, k8s:container-name
value: <workload-namespace> # the *workload's* namespace — NOT hush-security
- type: k8s:pod-label
key: <label-key> # required only for k8s:pod-label
value: <label-value>
deliveryConfig:
type: env # one of: env, volume, sdk, aws_wif, gcp_wif, azure_wif
config:
<delivery-type-specific>
enabled interacts with drift correctionThe Hush operator reconciles the policy on the platform back to whatever the manifest declares — if someone changes a managed field via the Hush API, UI, or Terraform, the operator restores it. enabled is the one field this can be opted out of, because flipping a policy on/off is often an operational action (e.g. on-call temporarily disabling a misbehaving policy) that shouldn't fight the controller.
The rule is based on whether spec.enabled is present in the manifest:
enabled omitted (default, recommended) — the operator does not reconcile this field. Anyone can flip the policy on/off via the Hush API/UI/Terraform without the operator restoring it. Use this when the manifest is the source of truth for what the policy is, but enable/disable is controlled elsewhere.enabled: true or enabled: false — the operator reconciles this field too. If it drifts (someone disables a policy declared enabled: true), the operator flips it back. Use this when you want declarative control over enable/disable as well, e.g. when the policy is defined alongside an app deployment that should always start enabled.All other policy fields (accessCredentialRef, accessPrivilegeRefs, attestationCriteria, deliveryConfig, etc.) are always reconciled — drift on those fields is always restored to the manifest value.
Default to omitting enabled. Only include it when the user explicitly says they want declarative on/off control.
accessCredentialRef and each entry of accessPrivilegeRefs accept exactly one of name or id — not both, not neither.
name when the credential or privilege is also being managed in Kubernetes as a CR in the same namespace. The operator resolves it to its underlying ID.id when the credential or privilege was created outside Kubernetes (e.g. via the Hush API or UI) and exists only on the platform side, not as a CR.You can mix the two within a single policy — e.g. reference the credential by name (managed via CR in the same namespace) and the privilege by id (managed externally), or vice versa.
# Both managed in K8s — refer by CR name
accessCredentialRef:
name: pg-prod
accessPrivilegeRefs:
- name: pg-readonly
# Both managed externally — refer by platform ID
accessCredentialRef:
id: acr_abc123
accessPrivilegeRefs:
- id: apr_xyz789
# Mixed — credential is a CR, privilege was created via API/UI
accessCredentialRef:
name: pg-prod
accessPrivilegeRefs:
- id: apr_xyz789
When the user is unclear, default to name if you're also generating the matching credential/privilege CR, and id if they tell you the resource already exists on the platform.
For these credential types, the AccessPolicy MUST omit accessPrivilegeRefs:
plaintext, kv — static credentials.gemini, bedrock — privileges are not modeled for these providers.aws_wif, gcp_wif, azure_wif — workload identity federation; access is enforced by the cloud provider.For all other dynamic types, the policy requires exactly one privilege of the matching type.
# env — inject as environment variables
deliveryConfig:
type: env
config:
items: # min 1
- name: POSTGRES_USERNAME # env var name; must match ^[a-zA-Z_][a-zA-Z0-9_]*$
key: username # credential field name
type: key # or "template" — see "Templated values" below for connection-string-style delivery
# volume — write into files at a mount point
deliveryConfig:
type: volume
config:
mount_point: /var/run/secrets/db # absolute path
items:
- path: username # relative path under mount_point
key: username
type: key
# sdk — fetched via the Hush SDK at runtime
deliveryConfig:
type: sdk
config:
secret_name: my-app-secret # must match ^[a-zA-Z0-9/_+=.@-]+$
items:
- name: db_user
key: username
type: key
# aws_wif — exchange for AWS credentials via STS
deliveryConfig:
type: aws_wif
config:
role_arn: arn:aws:iam::123:role/my-role
subject_kind: hush_subject # or service_account
subject: my-subject # required when subject_kind=hush_subject
# gcp_wif — exchange for GCP credentials
deliveryConfig:
type: gcp_wif
config:
subject_kind: hush_subject
subject: my-subject
service_account: [email protected]
service_account_token_lifetime: 3600
# azure_wif — exchange for Azure tokens
deliveryConfig:
type: azure_wif
config:
tenant_id: <uuid>
client_id: <client-id>
subject_kind: hush_subject
subject: my-subject
WIF constraints:
k8s:container-name attestation is not allowed with WIF delivery types.subject_kind: service_account requires both k8s:ns and k8s:sa attestation criteria.type and the delivery type must match for WIF (e.g. an aws_wif credential needs an aws_wif delivery).Each delivery item under env, volume, or sdk chooses between two type values:
type: key — key is a credential field name; the delivered value is the raw field value.type: template — key is a template string with ${field} placeholders; the delivered value is the rendered string.Templates use Python's string.Template syntax (${name} or $name). Every variable must be a valid field on the credential — both configured fields (e.g. host, port, db_name) and auto-generated fields (e.g. username, password for dynamic credentials).
# env delivery: a single DATABASE_URL env var built from credential fields
deliveryConfig:
type: env
config:
items:
- name: DATABASE_URL
key: "postgresql://${username}:${password}@${host}:${port}/${db_name}"
type: template
# volume delivery: write a connection string to a file
deliveryConfig:
type: volume
config:
mount_point: /var/run/secrets/db
items:
- path: connection-string
key: "postgresql://${username}:${password}@${host}:${port}/${db_name}"
type: template
You can mix key and template items in the same items[] list — e.g. expose username and password as separate env vars and a combined DATABASE_URL.
These are validated patterns matching each credential type's actual field names. Drop them into key: with type: template.
| Credential type | Connection string template |
|---|---|
postgres | postgresql://${username}:${password}@${host}:${port}/${db_name} |
mysql | mysql://${username}:${password}@${host}:${port}/${db_name} |
mariadb | mariadb://${username}:${password}@${host}:${port}/${db_name} |
mongodb | mongodb://${username}:${password}@${host}:${port}/${db_name}?authSource=${auth_source} |
mongodb_atlas | mongodb+srv://${username}:${password}@${host} |
redis | redis://${username}:${password}@${host}:${port}/${database} |
rabbitmq | amqp://${username}:${password}@${host}:${port}/${vhost} |
elasticsearch | http://${username}:${password}@${host}:${port} |
snowflake | snowflake://${username}@${account}/${database}/${schema}?warehouse=${warehouse}&role=${role} |
Notes:
${db_name} for postgres, not ${db}).mongodb_atlas, username/password are auto-generated (Atlas does not take user-supplied DB credentials), so they're available in templates even though they're not in the credential's config.snowflake with auth_method: key_pair, the ${password} placeholder won't resolve — use a separate item or omit credential auth from the connection string.redis with engine: elasticache, the ${password} placeholder is unavailable (ElastiCache uses AWS auth) — adjust the template accordingly.Each supported credential type has its own reference file in references/<type>.md. The file documents that type's credential config/secretRef shape, the matching privilege schema (or "no privilege type" note), the auth-principal permissions Hush needs, and any type-specific notes (auth method choices, connection-string templates, etc.).
Workflow: once the user has confirmed the type, you MUST read the matching references/<type>.md before emitting any manifest or asking type-specific questions. Don't try to recall fields from memory — types evolve and field names matter. The file is small and self-contained.
| Type | Privilege? | Reference file | Notes |
|---|---|---|---|
plaintext | no | references/plaintext.md | Static; single secret value |
kv | no | references/kv.md | Static; multiple named secret values |
postgres | yes | references/postgres.md | DB |
mysql | yes | references/mysql.md | DB |
mariadb | yes | references/mariadb.md | DB |
mongodb | yes | references/mongodb.md | DB |
mongodb_atlas | yes | references/mongodb_atlas.md | DB; OAuth or API Key auth |
redis | yes | references/redis.md | KV; redis or elasticache engine |
elasticsearch | yes | references/elasticsearch.md | Search |
rabbitmq | yes | references/rabbitmq.md | Messaging |
snowflake | yes | references/snowflake.md | Data warehouse; password or key_pair auth |
openai | yes | references/openai.md | AI |
gemini | no | references/gemini.md | AI; GCP-backed; SA-bound option |
bedrock | no | references/bedrock.md | AWS-backed AI |
grok | yes | references/grok.md | AI |
gcp_sa | yes | references/gcp_sa.md | GCP Service Account; federated or uploaded auth |
azure_app | yes | references/azure_app.md | Azure Application |
aws_access_key | yes | references/aws_access_key.md | AWS IAM users |
apigee | yes | references/apigee.md | GCP Apigee; federated or uploaded auth |
datadog | yes | references/datadog.md | + see references/datadog-scopes.md |
gitlab | yes | references/gitlab.md | |
salesforce | yes | references/salesforce.md | |
sendgrid | yes | references/sendgrid.md | + see references/sendgrid-scopes.md |
twilio | yes | references/twilio.md | + see references/twilio-permissions.md |
aws_wif | no | references/aws_wif.md | Federation |
gcp_wif | no | references/gcp_wif.md | Federation |
azure_wif | no | references/azure_wif.md | Federation |
Types marked no in the Privilege column don't take an AccessPrivilege — the AccessPolicy for these MUST omit accessPrivilegeRefs.
Some privilege types have very long enumerations (hundreds of scopes/commands/permissions). The per-type reference file links to these as needed:
references/datadog-scopes.md — full Datadog scope listreferences/sendgrid-scopes.md — full SendGrid scope listreferences/twilio-permissions.md — full Twilio permission pathsreferences/redis-commands.md — full Redis command list (categories are inline in redis.md)references/snowflake-privileges.md — full Snowflake privilege map per resource typeLoad these only when generating that specific privilege type — the per-type file will tell you when.
When a new credential type is added to Hush, add a new references/<type>.md following the same structure as existing files (Credential / Privilege / Required permissions on the auth principal / type-specific notes). Then add a row to the table above. No other files need to change.
config (non-sensitive) and secretRef (sensitive), per references/<type>.md.keyMappings.keyMappings for every sensitive field (identity map is fine). This avoids the "extra keys cause API failure" footgun.deliveryConfig and remember to omit accessPrivilegeRefs for the no-privilege types.apiVersion/kind/metadata.namespace: hush-security. Default to multi-document YAML (--- separator) when generating cred + privilege + policy together. Companion Secret documents must also live in hush-security so the operator can read them.kind: Secret document alongside the credential, or note that the user must run kubectl create secret generic <name> --from-literal=<key>=<value> first.references/<type>.md — every per-type file has a "Required permissions on the auth principal" section. If the per-type file lacks one or marks it "not applicable", say so explicitly to the user — don't invent permissions.accessCredentialRef and each entry of accessPrivilegeRefs accept exactly one of name or id (not both).attestationCriteria.key is required iff type: k8s:pod-label; it must be omitted for the other criterion types.^[a-zA-Z_][a-zA-Z0-9_]*$; sdk uses ^[a-zA-Z0-9/_+=.@-]+$; volume path is relative, cannot contain .., and cannot contain hush.security._HUSH and __HUSH are rejected.plaintext, kv) cannot be referenced by a policy that has accessPrivilegeRefs.type must match credential type.aws_wif ↔ aws_wif, etc.).keyMappings → API failure. Without keyMappings, every key in the K8s Secret is forwarded to the Hush API; any unrecognized key causes the create/update to fail. Emit keyMappings (identity is fine) whenever you reference a Secret you didn't generate.Secret) MUST live in hush-security. The operator only watches that namespace. Resources placed elsewhere are simply ignored — no error, just nothing happens. This is a hard rule; never override.apiVersion: v1
kind: Secret
metadata:
name: pg-creds
namespace: hush-security
type: Opaque
stringData:
password: <pg-password>
---
apiVersion: am.hush.security/v1alpha1
kind: AccessCredential
metadata:
name: pg-prod
namespace: hush-security
spec:
name: pg-prod
type: postgres
config:
db_name: app
host: pg.internal
port: 5432
ssl_mode: require
username: app_user
secretRef:
name: pg-creds
---
apiVersion: am.hush.security/v1alpha1
kind: AccessPrivilege
metadata:
name: pg-readonly
namespace: hush-security
spec:
name: pg-readonly
type: postgres
config:
grants:
- privileges: [SELECT]
object_type: TABLE
object_names: [public]
all_in_schema: true
---
apiVersion: am.hush.security/v1alpha1
kind: AccessPolicy
metadata:
name: pg-app-policy
namespace: hush-security
spec:
name: pg-app-policy
# enabled is omitted — toggle via API/UI/Terraform without operator restoring drift
accessCredentialRef:
name: pg-prod
accessPrivilegeRefs:
- name: pg-readonly
attestationCriteria:
- type: k8s:ns
value: app-namespace
- type: k8s:sa
value: app-sa
deliveryConfig:
type: env
config:
items:
- name: PG_USER
key: username
type: key
- name: PG_PASSWORD
key: password
type: key
When pg-creds already exists and may contain other keys (e.g. username, created_at, rotation metadata), drop the inline Secret document above and use keyMappings on the credential to filter and map explicitly:
apiVersion: am.hush.security/v1alpha1
kind: AccessCredential
metadata:
name: pg-prod
namespace: hush-security
spec:
name: pg-prod
type: postgres
config:
db_name: app
host: pg.internal
port: 5432
ssl_mode: require
username: app_user
secretRef:
name: pg-creds
keyMappings:
password: db_password # or `password: password` if the existing key matches —
# still required to filter out unrelated keys in the Secret
GCP auth via identity federation, generated API keys unbound — the simplest combination. No secretRef.
apiVersion: am.hush.security/v1alpha1
kind: AccessCredential
metadata:
name: gemini-prod
namespace: hush-security
spec:
name: gemini-prod
type: gemini
config:
project_id: my-gcp-project
service_account_bound: false
---
apiVersion: am.hush.security/v1alpha1
kind: AccessPolicy
metadata:
name: gemini-app-policy
namespace: hush-security
spec:
name: gemini-app-policy
# enabled is omitted — toggle via API/UI/Terraform without operator restoring drift
accessCredentialRef:
name: gemini-prod
# NO accessPrivilegeRefs — gemini does not take privileges
attestationCriteria:
- type: k8s:ns
value: app-namespace
deliveryConfig:
type: env
config:
items:
- name: GEMINI_API_KEY
key: api_key
type: key
GCP auth via uploaded service account key, generated API keys bound to a Hush-managed service account. Both decisions opted in.
apiVersion: v1
kind: Secret
metadata:
name: gemini-gcp-sa
namespace: hush-security
type: Opaque
stringData:
service_account_key: |
{
"type": "service_account",
...
}
---
apiVersion: am.hush.security/v1alpha1
kind: AccessCredential
metadata:
name: gemini-prod
namespace: hush-security
spec:
name: gemini-prod
type: gemini
config:
project_id: my-gcp-project
service_account_bound: true # bind generated API keys to a created GCP SA
secretRef:
name: gemini-gcp-sa # Hush uses this SA key to provision API keys
When the credential and privilege already exist on the Hush platform (created via API or UI), the policy refers to them by id — no companion CRs needed.
apiVersion: am.hush.security/v1alpha1
kind: AccessPolicy
metadata:
name: external-pg-policy
namespace: hush-security
spec:
name: external-pg-policy
enabled: true # included here to demonstrate K8s-managed enable/disable; the operator will restore drift on this field
accessCredentialRef:
id: acr_01h8z9p7q2r5x3v6t4y2k1m9w8
accessPrivilegeRefs:
- id: apr_01h8z9p7q2r5x3v6t4y2k1m9w9
attestationCriteria:
- type: k8s:ns
value: app-namespace
- type: k8s:sa
value: app-sa
deliveryConfig:
type: env
config:
items:
- name: PG_USER
key: username
type: key
- name: PG_PASSWORD
key: password
type: key
Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub hushsecurity/agent-skills --plugin hush-uam