hkHarness Kit
Concepts

Secrets & Configuration

Using a coding agent? Install the Harness Kit docs as a skill:
npx skills add https://github.com/harnessprotocol/harness-kit --skill harness-docs

Secrets & Configuration

The Problem

Plugins need external credentials — API keys, personal access tokens, service passwords. As harness-kit grows, the list of potential dependencies grows with it. But harness-kit can't mandate a secrets provider, and hardcoding credentials into config files is a security incident waiting to happen.

The question isn't whether plugins need secrets. It's how to handle that dependency cleanly.

The Contract: Environment Variables

harness-kit uses environment variables as the universal contract for secrets:

  • Plugins declare what they need (name, description, whether it's required)
  • Users provide the value however they prefer — shell profile, .envrc, a secrets manager
  • harness-kit validates that required variables exist before running (it never reads or logs the value)

One-liner: Plugins declare, users provide, harness-kit validates.

Why Environment Variables

Environment variables are the right primitive for secrets:

PropertyWhy it matters
UniversalEvery OS, CI system, and container runtime supports them
Provider-agnostic1Password, Google Secret Manager, HashiCorp Vault, direnv — all inject via env vars
Cross-harness compatibleClaude Code, Copilot, Cursor all read the same environment
Familiar12-factor app methodology; what engineers already expect

Your secrets infrastructure doesn't change. You just configure which variables to set.

The requires.env Schema

Plugins declare environment requirements in plugin.json under requires.env:

{
  "name": "my-plugin",
  "version": "0.1.0",
  "requires": {
    "env": [
      {
        "name": "MY_API_KEY",
        "description": "API key for My Service — used to authenticate requests",
        "required": true,
        "sensitive": true
      },
      {
        "name": "MY_SERVICE_URL",
        "description": "Base URL for My Service — defaults to production if unset",
        "required": false,
        "sensitive": false,
        "when": "Connecting to a non-default My Service endpoint"
      }
    ]
  }
}

Field Reference

FieldTypeRequiredDefaultPurpose
namestringyesEnv var name (uppercase, underscored)
descriptionstringyesOne sentence: what it's for and how it's used
requiredbooleannofalsetrue = plugin can't function without it; false = graceful degradation
sensitivebooleannotruetrue = secret (token, password); false = plain config (URL, flag)
whenstringnoDescribes the specific code path that needs this var

required vs optional

  • required: true — The plugin cannot run without this variable. harness-kit will surface an error before attempting to execute.
  • required: false (default) — The plugin degrades gracefully. The SKILL.md describes what's skipped and what the user can do to enable full functionality.

sensitive vs config

  • sensitive: true (default) — The value is a secret: a token, password, or key. harness-kit treats these as opaque and never logs or displays them.
  • sensitive: false — The value is plain configuration: a URL, feature flag, or numeric setting. Non-sensitive vars can appear in diagnostics.

Security Principles

Five rules for handling secrets in plugins:

  1. Never store secrets in SKILL.md or plugin.json — declare the variable name, not the value
  2. Never commit .env files — add them to .gitignore and document this in your plugin's README
  3. Least privilege — declare only the scopes your plugin actually needs. If you only read PRs, don't request write access.
  4. harness-kit never logs values — validation checks for existence only; the value is never read by the framework
  5. sensitive: true suppresses diagnostics — sensitive vars are excluded from any debug output or status displays

Future: Dependency Graph

requires.env is one dimension of a plugin's dependency contract. The requires object is designed to grow:

{
  "requires": {
    "env": [...],
    "tools": ["gh", "python3"],
    "mcp": ["memory-server"],
    "plugins": ["research@>=0.2.0"]
  }
}

Each sibling key follows the same pattern: plugins declare, users provide, harness-kit validates. The requires.env schema establishes the convention.

See Also

On this page