How clictl handles credentials, tokens, and secrets. Designed so sensitive data never leaks into tool specs, logs, or version control.
- Secrets stay in environment variables or .env files. Tool specs reference variable names, never actual values.
- Credentials are stored with restrictive permissions. Config files use 0600 (owner read/write only).
- Tokens are short-lived. Access tokens expire in 60 minutes. Refresh tokens expire in 7 days.
- Nothing sensitive goes over the wire unless encrypted. All API communication uses HTTPS.
Tools declare which environment variables they need. You set the values. clictl injects them at execution time.
Example tool spec:
# spec: "1.0" (optional, defaults to "1.0")
name: open-meteo
auth:
type: api_key
key_env: GITHUB_TOKEN
inject:
location: query
param: appidYou set:
export GITHUB_TOKEN=your-actual-keyclictl does: reads GITHUB_TOKEN from your environment, injects it into the request as the appid query parameter. The key never appears in the tool spec, skill file, or logs.
| Auth type | Env var example | How it's injected |
|---|---|---|
| API key (query) | GITHUB_TOKEN |
?appid=<value> |
| API key (header) | ANTHROPIC_API_KEY |
x-api-key: <value> |
| Bearer token | GITHUB_TOKEN |
Authorization: Bearer <value> |
| Basic auth | SERVICE_CREDENTIALS |
Authorization: Basic <base64> |
If a tool requires GITHUB_TOKEN and it's not set, clictl shows:
Error: open-meteo requires GITHUB_TOKEN
Set it with: export GITHUB_TOKEN=your-token
No fallback, no prompt. The variable must be present before execution.
clictl loads .env files automatically. This keeps secrets out of your shell history and makes them easy to manage per project.
Load order (first found wins):
.envin the current directory.envin the project root (nearest parent with.git)~/.clictl/.env(global defaults)
Format:
# .env
GITHUB_TOKEN=abc123
GITHUB_TOKEN=ghp_xxxxxxxxxxxx
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxRules:
- One variable per line,
KEY=VALUEformat - Lines starting with
#are comments - No quotes needed around values (but single or double quotes are stripped if present)
- Empty lines are ignored
- Variables set in the shell environment take precedence over .env values
Important: Add .env to your .gitignore. clictl will warn if it detects a .env file that is tracked by Git.
Your clictl account credentials (for registry access, publishing, etc.) are stored separately from tool secrets.
Location: ~/.clictl/config.yaml (permissions: 0600)
auth:
api_key: "CLAK-..." # personal API key
access_token: "eyJ..." # JWT access token (60 min)
refresh_token: "eyJ..." # JWT refresh token (7 days)
expires_at: "2026-03-21T..." # when access_token expiresToken lifecycle:
clictl loginopens browser, completes OAuth, stores tokens- Access token is sent with API requests
- When it expires, clictl uses the refresh token to get a new one automatically
- If the refresh token expires, you need to
clictl loginagain
API key alternative:
API keys can be passed as a flag to any command or set as an environment variable:
clictl search weather --api-key CLAK-your-key-here
# or
export CLICTL_API_KEY=CLAK-your-key-hereAPI keys don't expire (unless you revoke them). Create them at your workspace settings page.
Precedence: --api-key flag > CLICTL_API_KEY env var > config api_key > config access_token
Memories (notes your agent attaches to tools) are stored locally:
Location: ~/.clictl/memory/
Memories are plain JSON files, one per tool. They contain no secrets, just text notes your agent has written. They are not synced, uploaded, or shared.
Cached tool specs are stored at ~/.clictl/cache/. These are copies of publicly available spec files and contain no secrets. The cache uses ETag-based validation and can be cleared with:
rm -rf ~/.clictl/cache/Or bypass it per-request:
clictl run open-meteo current --no-cache --q LondonWhen installing skills that include sha256 hashes in source.files, clictl verifies each downloaded file against the expected hash before writing it to disk.
How hashes are computed: The registry computes SHA256 hashes during registry sync by reading each file from the source URL and hashing its contents. Skill authors can also generate hashes locally using clictl skill manifest <dir>, which scans a directory and outputs a files: array with per-file SHA256 hashes ready to paste into a spec.
What happens on mismatch: If a downloaded file's SHA256 hash does not match the value in the spec, the install is aborted immediately with an error showing the expected and actual hashes. No files are written to disk for that skill.
Missing hashes: If a file in source.files has no sha256 field, clictl prints a warning but continues the install. Skill authors should always include hashes for published specs.
Tool specs can include JavaScript transforms that run via the goja engine. These are sandboxed:
- No network access.
fetch,XMLHttpRequest,WebSocketare all blocked. A transform cannot call out to external services or exfiltrate credentials. - No code generation.
evaland theFunctionconstructor are blocked. Scripts cannot dynamically generate and execute code. - No I/O. No filesystem, no
console, nolocalStorage. The script receives data in and returns data out. - No modules.
requireandimportare blocked. - 5-second timeout. Scripts that run too long are terminated.
- Pre-execution validation. Patterns like
new Function(and.constructor(are rejected before the script even runs.
Safe operations (array methods, string manipulation, Math, JSON, object construction) all work normally.
When clictl spawns MCP server subprocesses (via npx, uvx, etc.), they run in a sandboxed environment to protect against supply chain attacks from compromised packages.
Subprocess environment is built from an allowlist, not inherited from the parent:
- Essential system vars:
PATH,HOME,TMPDIR,LANG,USER,SHELL,TERM(plus Windows equivalents) - Spec-declared vars: Only env vars listed in
permissions.envare passed through - Auth vars: Only env vars configured in
auth[].key_envare injected - Transport vars: Literal values from
transport.envin the spec - Marker:
CLICTL_SANDBOX=1is always set so processes can detect sandboxing
Everything else (AWS credentials, GitHub tokens, database URLs, etc.) is stripped unless the spec explicitly declares it.
- Linux (kernel 5.13+): Landlock allowlist restricts filesystem access to declared paths only. Sensitive directories (
~/.ssh,~/.aws,~/.gnupg, browser profiles, crypto wallets) are implicitly denied. - macOS:
sandbox-execwith a generated Scheme profile applies read/write restrictions. - Windows: Job Objects contain the process tree (child processes cannot outlive the parent).
Sandbox is on by default and operates in fail-closed mode (StrictSandbox config option). If the sandbox cannot be initialized (e.g., missing platform support), execution is blocked rather than proceeding unsandboxed. To disable:
# ~/.clictl/config.yaml
sandbox: falseOr per-invocation:
clictl mcp-serve --no-sandboxWorkspace admins on enterprise plans can enforce sandboxing via policy, preventing members from disabling it.
If the platform does not support the sandbox mechanism (e.g., Landlock on an older kernel, sandbox-exec with SIP disabled), clictl logs a warning and proceeds with env scrubbing only.
Skills execute inside the AI agent's process, not as sandboxed subprocesses like MCP servers. This means a skill has the same capabilities as the agent itself unless explicitly restricted. Skill isolation addresses this by layering multiple restrictions on top of skill execution.
When an agent loads a skill, the skill's instructions run with the agent's full tool access by default. A malicious or misconfigured skill could read sensitive files, execute arbitrary commands, or exfiltrate data through bash. Unlike MCP servers (which run in a separate process with OS-level sandboxing), skills require application-level isolation enforced by the agent and CLI together.
Skill isolation uses a defense-in-depth approach with multiple independent layers:
Layer 1: Tool Restriction (free)
Skills declare which agent tools they need via requires_tools in the spec. During installation, this becomes the allowed-tools frontmatter in the generated SKILL.md. The agent only grants the skill access to listed tools. A skill that declares requires_tools: [Bash, Read] cannot use Write, Edit, or Grep.
Layer 2: Filesystem Scope (free)
Skills declare permissions.filesystem.read and permissions.filesystem.write paths. These are embedded in the generated skill file and enforced during execution. The skill can only read or write within the declared paths. Paths outside the scope are blocked.
Layer 3: Bash Allowlisting (enterprise)
Skills declare bash_allow patterns that restrict which shell commands the skill can run. Glob matching is supported (e.g., clictl run * allows any clictl run invocation). Commands not matching any pattern are rejected. This prevents arbitrary shell execution even when the Bash tool is available.
Layer 4: Network Restriction (team+)
The permissions.network list restricts which hosts the skill can communicate with. Outbound requests to unlisted hosts are blocked.
Layer 5: Skill Signing (enterprise) Publishers sign skills with Ed25519 keys registered with the registry. During installation, clictl verifies the signature. Workspaces can require all installed skills to be signed, rejecting unsigned or tampered skills.
| Layer | Free | Team | Enterprise |
|---|---|---|---|
| Tool restriction | Yes | Yes | Yes |
| Filesystem scope | Yes | Yes | Yes |
| Network restriction | - | Yes | Yes |
| Bash allowlisting | - | - | Yes |
| Skill signing enforcement | - | - | Yes |
| Skill permission overrides | - | Yes | Yes |
| Managed skill sets | - | - | Yes |
| Skill audit logging | - | Yes | Yes |
The MCP process sandbox and skill isolation serve different purposes:
- MCP sandbox protects the host OS from a compromised MCP server subprocess. It uses OS-level mechanisms (Landlock, sandbox-exec, Job Objects) and environment scrubbing.
- Skill isolation protects the agent session from a malicious or overprivileged skill. It uses application-level restrictions (tool filtering, filesystem scope, bash allowlisting) enforced by the agent and CLI.
A tool that has both an MCP server and a skill component benefits from both layers. The MCP subprocess is sandboxed at the OS level, and the skill instructions are restricted at the application level.
- Do not put secrets in tool spec YAML files. Use
key_envto reference environment variable names instead. - Do not commit
.envfiles. Add.envto.gitignore. - Do not share
~/.clictl/config.yaml. It contains your auth tokens. - Do not set secrets as default parameter values in specs. Parameters are visible to everyone.
- Do not use JavaScript transforms to handle secrets. Transforms process response data only. Auth injection happens before the transform runs.
Once a spec version is published, its content is locked. The registry rejects any attempt to overwrite an existing version. To make changes, bump the version number and publish again. This prevents supply-chain attacks where a published tool could be silently modified after users have installed it.
Specs can declare what permissions a tool requires using the permissions field. This tells users and agents exactly what external access a tool needs before installation.
permissions:
network:
- "api.github.com"
env:
- "GITHUB_TOKEN"The network list declares which hosts the tool communicates with. The env list declares which environment variables the tool reads. These declarations are informational and displayed during clictl install and clictl info so users can make informed decisions.
Users can report tools that are broken, malicious, or otherwise problematic:
clictl report some-tool --reason "Returns malicious output"Reports are sent to the registry. When a tool accumulates 3 or more reports, it is automatically disabled in the registry. Disabled tools cannot be installed or executed until a maintainer reviews and resolves the reports.
Use clictl audit to check your installed tools against the registry for any that have been reported or disabled.
Tool names use a namespace/tool-name format to prevent naming collisions across organizations. For example, acme/deploy and bigcorp/deploy can coexist without conflict. Namespaces use bare format with no @ prefix.
MCP server specs often point to packages hosted on npm or PyPI. Under the hood, clictl uses npx (Node) and uvx (Python) to run these packages. However, users never invoke npx or uvx directly. clictl manages the full lifecycle and adds several safety layers on top.
Version pinning. Every MCP spec pins the package to a specific version in its package field. clictl will not run a package at a floating or "latest" tag. This prevents surprise upgrades that could introduce malicious code.
package:
registry: npm
name: "@org/mcp-server"
version: "1.2.0"Package integrity. When a spec includes SHA256 hashes, clictl verifies the downloaded package content before execution. A mismatch aborts the install immediately.
Runtime detection. Before installing an MCP package, clictl checks whether the required runtime (Node or Python) is available on the system. If the runtime is missing, the install fails with a clear message and a link to install instructions, rather than producing a confusing error from a missing binary.
Permission declarations. Specs declare what network hosts, environment variables, and filesystem paths the package needs access to. These are displayed during clictl install so users can review exactly what they are granting.
The --trust flag. Packages from unverified publishers require the --trust flag to install. Without it, clictl refuses to proceed. This forces an explicit opt-in for packages that have not been reviewed by the registry.
What this means in practice: instead of running npx -y @some-org/unknown-package and hoping for the best, clictl ensures the package is pinned, hashed, scoped to declared permissions, and explicitly trusted before any code executes.
If you find a security issue, email security@clictl.com. Do not open a public issue.
See also: Securing Secrets | CLI Reference | Spec Format | Memory