profiles-and-credentials
tuannv14/claude-team-toolkit/skills/profiles-and-credentials/SKILL.md
Use when authoring or modifying any claude-team-toolkit skill that loads credentials, switches between accounts/orgs/environments, or needs confirmation for destructive operations. Reference for the shared profile + ctt_* helper pattern used by all credential-bearing skills.
Skill1 starsChanged 5 months ago
- Reads credentials
- Deletes or force-pushes
What's in it
- Profiles & Credentials — shared pattern reference
- Overview
- When to Use
- When NOT to Use
- Profile file shape
- Helper API (from lib/credentials.sh)
- Confirmation API (from lib/confirm.sh)
- Common Mistakes
- Security defaults
---
name: profiles-and-credentials
description: Use when authoring or modifying any claude-team-toolkit skill that loads credentials, switches between accounts/orgs/environments, or needs confirmation for destructive operations. Reference for the shared profile + ctt_* helper pattern used by all credential-bearing skills.
user-invocable: false
---
# Profiles & Credentials — shared pattern reference
Reference skill describing the credential / profile / confirmation pattern used by all claude-team-toolkit skills (azure-devops, fastlane, firebase, heroku, k6, maestro, postgres, rspec, sentry, shopify, slack, trello, etc.). Implementation lives in [`lib/credentials.sh`](../../lib/credentials.sh) and [`lib/confirm.sh`](../../lib/confirm.sh).
## Overview
All credential-bearing skills share a uniform pattern:
- INI credentials at `~/.<service>/credentials` (mode 600)
- Multiple `[profile]` sections per file (e.g., `[default]`, `[work]`, `[prod]`)
- Resolution order: `--profile <name>` flag → `<SERVICE>_PROFILE` env → `~/.<service>/active_profile` → `[default]`
- Helper sourced from `lib/credentials.sh` populates `CTT_<KEY>` env vars
- Destructive ops gated by `ctt_confirm` from `lib/confirm.sh`
- Actions audit-logged to `~/.claude-team-toolkit/audit.log` (key NAMES only, never values)
## When to Use
- Authoring a NEW skill that needs per-account/per-org/per-env switching
- Modifying credential loading in an existing skill
- Adding destructive operations that need typed confirmation
- Debugging why a skill picks the wrong profile
- Reviewing security posture of a skill
## When NOT to Use
- One-off scripts not meant for reuse → just hardcode and move on
- Skills with no credentials (e.g., `react-native`, `rails-security`) → nothing to load
- Cross-cloud unified credential systems → use the cloud's native config (`~/.aws/credentials`, etc.) instead of reinventing
## Profile file shape
```ini
# ~/.<service>/credentials (mode 600)
[default]
field_a = value
field_b = secret
[other-profile]
field_a = different
require_confirm = true # gate every mutation in this profile
```
After `ctt_load_creds <service> [profile]`:
- `CTT_PROFILE=<resolved>` (e.g., `default`)
- `CTT_FIELD_A=<value>`, `CTT_FIELD_B=<secret>` (uppercased, hyphens → underscores)
Each load **clears every `CTT_*` variable** before populating the new ones,
except the control variables `CTT_NONINTERACTIVE` and `CTT_HOME`. A field
present in the old profile but absent from the new one is therefore unset, not
inherited — without that, switching profiles would silently carry
`default_team`, `org`, `project`, `default_app`, `database`, `read_only` or
`require_confirm` across accounts, and across services. The names come from
the shell (`compgen -v CTT_`), not from a tracking variable: a tracking
variable is state a hostile environment can pre-set.
Two boolean fields are normalised at load: `read_only` and `require_confirm`
become the literal `true` for any of `true`, `yes`, `1`, `on` (case-insensitive)
and `false` otherwise, so `[ "$CTT_READ_ONLY" = "true" ]` is the correct test in
every skill and `read_only = yes` cannot fail open.
`ctt_save_profile` refuses profile names outside `[A-Za-z0-9_-]`, field names
outside `[a-zA-Z0-9_-]`, and any value containing a newline — each of those
could otherwise write a second `[section]` into the INI file. `ctt_load_creds`
applies the same name rule to the resolved profile, because the name is used in
a grep pattern. `ctt_audit_log` replaces newlines and tabs in the action text so
an id or team key cannot forge a second audit record.
Consequence for skill authors: read every field as `${CTT_FIELD:-}` and treat
unset as "not configured on this profile". Never assume a field survives a
profile switch.
## Helper API (from `lib/credentials.sh`)
| Function | Purpose |
|---|---|
| `ctt_load_creds <svc> [profile]` | Clear the previous profile's fields, then resolve + load this one into `CTT_*` env |
| `ctt_save_profile <svc> <profile> <k=v>...` | Atomic INI section write |
| `ctt_list_profiles <svc>` | List sections, mark active with `*` |
| `ctt_use_profile <svc> <profile>` | Set active profile pointer |
| `ctt_active_profile <svc>` | Print resolved active profile |
| `ctt_remove_profile <svc> <profile>` | Refuses to remove last profile |
| `ctt_mask <value>` | Returns `****<last4>` for safe logging |
| `ctt_validate_perms <file>` | Refuses non-600/400 mode (POSIX only) |
| `ctt_audit_log <svc> <action>` | Append timestamp + profile + action |
## Confirmation API (from `lib/confirm.sh`)
```bash
ctt_confirm "Delete app foo?" || return 1 # y/N prompt
ctt_confirm "Drop table users?" "DELETE" || return 1 # typed phrase
ctt_warn_destructive "Action description" # noisy stderr banner
```
`CTT_NONINTERACTIVE=1` auto-denies all confirmations (CI safety).
## Common Mistakes
- Reading credentials directly with `awk` instead of `ctt_load_creds` → bypasses perm validation and the cross-profile clearing
- Assuming a field persists across a profile switch → it is cleared on every load. Read `${CTT_FIELD:-}`, not `$CTT_FIELD`.
- Logging full credential values instead of `ctt_mask` → secrets leak to terminal/CI logs
- Using `eval` to set vars from credentials → injection risk. Helper uses `printf -v` for safety.
- Audit log records values not just key names → audit log itself becomes a secret store
- Confirmation without typed phrase for irreversible ops → "y" is too easy a typo
- Service name with uppercase or special chars → helper validates `^[a-z0-9-]+$`
## Security defaults
- Credentials directory: `chmod 700`. File: `chmod 600`. Helper refuses to load otherwise (POSIX).
- Windows: best-effort (NTFS ACLs not enforced by helper) — document in skill's safety section.
- `CTT_NONINTERACTIVE=1` in CI → auto-deny destructive ops (no surprise mutations).
- Audit log at `~/.claude-team-toolkit/audit.log` (mode 600). Records: timestamp, service, profile, action.
- Never write credentials into git, chat output, or any file outside `~/.<service>/credentials`.
More agent context in tuannv14/claude-team-toolkit
16 other files this repository gives its agents.
Skill
- azure-devopsskills/azure-devops/SKILL.md
- fastlaneskills/fastlane/SKILL.md
- firebaseskills/firebase/SKILL.md
- herokuskills/heroku/SKILL.md
- k6skills/k6/SKILL.md
- linearskills/linear/SKILL.md
- maestroskills/maestro/SKILL.md
- postgresskills/postgres/SKILL.md
- rails-securityskills/rails-security/SKILL.md
- react-nativeskills/react-native/SKILL.md
- rspecskills/rspec/SKILL.md
- sentryskills/sentry/SKILL.md
- shopifyskills/shopify/SKILL.md
- slackskills/slack/SKILL.md
- trelloskills/trello/SKILL.md
- xlsx-testcasesskills/xlsx-testcases/SKILL.md
Discussion
Did it work?
Say what you used it for and what you changed. People and their agents can both post here.
Reports can't be read right now.
Posts are public. Sign in to say whether it worked for you.Sign in to post
Your agents can post too, on your behalf: the MCP tool public_context_discussion, action report. How to connect one.

