---
title: "Binding"
description: "Who acts on each exec and which credentials the runtime mints for it, so a pack reaches a protected system without ever holding a secret."
canonical: https://harnesscontextprotocol.io/docs/concepts/binding
---

# Binding

> Who acts on each exec and which credentials the runtime mints for it, so a pack reaches a protected system without ever holding a secret.

A pack declares the kind of credential its commands need, and the runtime's hub
mints it at exec time for the Principal who asked. The pack never sees, stores
or ships a secret, and a failed mint refuses the command instead of falling back
to a token exported in the shell ([L4](/docs/introduction/design-principles)).
Pack authors choose a mode below; runtime builders wire the hub and the Principal.

```text
pack declares  binding.mode = user
                     │
runtime hub    mints delegated credential for this Principal
                     │
adapter        receives it as env or file the wrap bin already understands
```

## Modes

A pack declares one mode, so two faces of a CLI that need different modes are
two packs ([L9](/docs/introduction/design-principles)).

| Mode | Credential | Runtime expectation | Typical pack |
|------|------------|---------------------|--------------|
| `public` | None, and connect is skipped | The Principal is still attributed | Public read (`transit`) |
| `user` | Delegated user credential (OAuth, user token) | Minted for this Principal | Linked channel (mail, calendar) |
| `local` | Machine-local credential hub (dev, appliance, personal workspace) | The Principal is the machine's user | Gated write (`ledger`) |
| `mothership` | Operator or fleet identity | Elevation grant for any non-operator Principal | Control plane, as a second pack |
| `mock` | Explicit mock in the exec envelope | Never ambient; env-flag mocks fail closed in sandbox | Tests, paper mode |

## Hubs

The hub is where the credential lives, and the pack names only its kind:

| `hub` | Meaning |
|-------|---------|
| `keyring` | A product credential service |
| `local-credential` | A machine-local store |
| `public` | No credential |
| `none` | Pack needs none and declares so |

The protocol requires only that the hub mints fail-closed and that Invokers mint
through it. Its name, token storage and refresh are runtime details with no HCP
type.

## Families

Related packs share one login by declaring the same `family`, so a user who
connected `mail` is not asked again for `calendar` on the same account.

```json
"binding": { "mode": "user", "hub": "keyring", "family": "office" }
```

Connecting one pack connects the whole family for that Principal with one
consent, and linking several packs unions their delegated scopes. A family is a
manifest field, so packs stay flat under `packs/<id>/`.

## Connect

Establishing a Binding is itself a command (`connect`, or a `setup.*` step) with
`operation: write`. It is gated by `pack.connect`, separate from the
`verb.exec.write` grant for daily writes, because sending mail must not imply
re-linking the mailbox. Domain CLIs that split control-plane link from runtime
send should keep two grants as well.

## Health

A Binding degrades when a refresh token expires or a provider revokes consent.
Every pack with a `user`, `local` or `mothership` Binding should then announce
the [Sense](/docs/concepts/sense) trigger `binding.degraded`, which the runtime's
hub usually produces, with one state:

```text
linked | stale | needs_reauth | pending | error
```

A human then runs `connect`. Health commands (`doctor`, `status`) report the
state and never mint a long-lived secret as a side effect.

## Principal

The Principal is who acts when a command runs: `human` (an authenticated person
or profile), `service` (an API key or automation), `agent` (acting on behalf of
a human or service) or `mothership` (operator or fleet identity). The kinds are
informative. The runtime establishes exactly one Principal per exec from the
authenticated session, records it and evaluates grants for it, so the exec
envelope never carries one and packs never embed, name or allow-list one.

The tenant, the organisation or workspace boundary, comes from the same session
and never from a pack argument. A command whose `privacy.land` is `brain` also
needs a project, the shared work context ([lands](/docs/concepts/access#authorization)).
Arguments refine within that scope, so `--entity acme-eu` may select a linked
account under the tenant but never the tenant itself, or one user could read
another's data.

The Principal answers who asked for this exec, and Binding answers which
credentials may be minted for this pack, mode and family. They stay separate
columns in the [audit tuple](/docs/concepts/access#audit), since one string for
both hides the classic elevation bug: an end-user Principal reaches an operator
Binding because a `mothership` bin was on `PATH`. A runtime must refuse that
pairing without an explicit elevation grant.

The reference SDK takes the Principal and its scope at creation:

```ts
const hcp = await Hcp.create({
  packDirs,
  principal: { id: 'user_42', kind: 'human' },
  tenant_id: 'acme',
  project_id: 'proj_7',   // needed for any brain-land command
  grants,
})
```

Its default authoriser denies a `brain` command when `project_id` is null:

```json
{ "ok": false, "error": "denied: privacy.land brain requires project_id" }
```

## What stays out of the pack

- Tokens, refresh secrets, client ids, key files
- Tenant ids and account allow-lists
- The hub's table names, provider SDK calls, or refresh schedule

## Related

- [Specification: Binding](/docs/specification/binding)
- [Specification: Authorization](/docs/specification/authorization)

## Sitemap

- [Documentation](https://harnesscontextprotocol.io/docs)
- [Packs](https://harnesscontextprotocol.io/packs)
- [Glossary](https://harnesscontextprotocol.io/docs/reference/glossary)
- [llms.txt](https://harnesscontextprotocol.io/llms.txt)
- [llms-full.txt](https://harnesscontextprotocol.io/llms-full.txt)
- [OpenAPI](https://harnesscontextprotocol.io/openapi.json)
- [MCP](https://harnesscontextprotocol.io/mcp)
