Metadata-Version: 2.4
Name: connection-hub-cli
Version: 2026.10.25.200
Summary: KDCube host setup, credential custody, and MCP client integration for Connection Hub.
Author: Elena Viter
License-Expression: MIT
Project-URL: Homepage, https://github.com/elenaviter/app-ecosystem
Project-URL: Repository, https://github.com/elenaviter/app-ecosystem
Project-URL: Documentation, https://github.com/elenaviter/app-ecosystem/tree/main/docs/connection-hub
Project-URL: Issues, https://github.com/elenaviter/app-ecosystem/issues/3
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: app-foundation[mcp,native-secrets]<2027,>=2026.10.25.0100
Requires-Dist: connection-hub[client]<2027,>=2026.10.25.0100
Requires-Dist: filelock<4,>=3.16
Requires-Dist: httpx2<3,>=2.5
Requires-Dist: json5<1,>=0.12
Requires-Dist: kdcube-cli<2027,>=2026.10.25.0130
Requires-Dist: keyring<26,>=25
Requires-Dist: mcp==2.0.0
Requires-Dist: platformdirs<5,>=4
Requires-Dist: PyYAML<7,>=6
Provides-Extra: test
Requires-Dist: pytest<10,>=8; extra == "test"
Requires-Dist: pytest-asyncio<2,>=0.23; extra == "test"

# Connection Hub CLI

`connection-hub-cli` starts or selects a KDCube-hosted Connection Hub,
authorizes exact management operations, and connects desktop MCP clients to a
governed MCP endpoint. The installed command is `connection-hub`.

```bash
uv tool install connection-hub-cli
connection-hub setup
```

`setup` creates a dedicated local KDCube runtime by default. It can also select
an existing local workdir or an HTTPS KDCube endpoint. New local setup uses
Google login by default and accepts `--auth simple` for an explicit development
environment.

## Connect An MCP Client

Two connection modes cover different credential owners:

| Mode | Remote transport | Credential custody |
| --- | --- | --- |
| `oauth` | Client connects directly by Streamable HTTP | The MCP client's OAuth store |
| `bridge` | Client starts `connection-hub mcp serve` over stdio | The current user's native operating-system store |
| `auto` | Selects a verified native OAuth path, or a supplied local profile | The selected mode's owner |

For Claude Code, Codex, Hermes, or OpenClaw with native remote OAuth:

```bash
connection-hub client install claude-code \
  --mode auto \
  --endpoint https://runtime.example/mcp
```

The JSON result includes the non-secret interactive authorization command.
Run it in the client's normal terminal. Claude Desktop remote connectors are
created in its Settings interface; the CLI manages its local stdio entry.
Direct OAuth does not construct or probe the Connection Hub native store, so
it remains available on headless Linux and WSL installations where the MCP
client has its own secure OAuth custody.

For a local bridge, first create a profile through browser OAuth and PKCE:

```bash
connection-hub profile authorize coding-agent \
  --endpoint https://runtime.example/mcp
connection-hub client install claude-code \
  --mode bridge \
  --profile coding-agent
```

For a headless host with a supported native credential store, use device
authorization. Open the printed verification URL on any browser-capable device;
the headless host opens no callback listener:

```bash
connection-hub profile authorize coding-agent \
  --endpoint https://runtime.example/mcp \
  --device
```

The bridge retrieves and refreshes the OAuth token set internally. The client
configuration contains only this command shape:

```text
connection-hub mcp serve --profile coding-agent
```

The default profile is an agent/automation profile. It stores the server-issued
Card kind and OAuth server identity, but no MCP entry resource; one credential
can therefore use every surface granted on that Card. Use `--resource-bound`
when creating a connector profile for one fixed MCP endpoint. Reconnect follows
the persisted server-issued Card kind and does not reclassify a profile.

An application embedding the profile-session API can identify one concrete
client instance during dynamic registration:

```python
await oauth_profile_sessions.authorize(
    name=profile_name,
    endpoint=endpoint,
    client_name="Connection Hub CLI · codex:session-1",
    client_metadata={
        "kdcube_agent_id": "codex:session-1",
        "kdcube_machine_id": "machine-1",
    },
)
```

These fields are sent only for dynamic client registration. Connection Hub
bounds and stores accepted values for Card display and search; neither the CLI
nor Connection Hub treats them as identity or access authority. Provisioned
and metadata-document clients already own their registration metadata and
therefore cannot be overlaid with a second client-supplied block here.

A manually issued short-lived delegated bearer can initialize a static bridge
profile through hidden input:

```bash
connection-hub profile add coding-agent \
  --endpoint https://runtime.example/mcp \
  --access-id access_example
```

Use `--credential-stdin` only when standard input already comes from a
controlled secret store. The profile metadata files contain an opaque
credential reference and optional `access_id`; bearer and OAuth token values
remain in native custody.

The accepted native backends are:

| Platform | Store | Exact backend |
| --- | --- | --- |
| macOS | login Keychain | `keyring.backends.macOS.Keyring` |
| Windows | Credential Manager | `keyring.backends.Windows.WinVaultKeyring` |
| Linux desktop | Secret Service | `keyring.backends.SecretService.Keyring` |

Windows values use a bounded, versioned chunk representation with digest
verification and transactional generation replacement. Its 288 KiB UTF-8
bound covers every token record accepted by this CLI, including worst-case
JSON escaping. Linux bridge mode requires a graphical session D-Bus, an
available Secret Service provider, and an unlocked default collection.
Backend selection fails closed and has no plaintext fallback.

Command-backed adapters verify add, login, logout, inspection, and removal
capabilities relevant to the selected mode before writing an entry. A client
version with an incomplete OAuth lifecycle is rejected; `auto` can use an
explicitly supplied bridge profile instead.

## Operate The Selected KDCube Host

Authorize the CLI through the identity provider configured by the selected
KDCube deployment:

```bash
connection-hub host authorize
connection-hub host inspect
connection-hub host surfaces connection-hub@1-0
connection-hub host reload connection-hub@1-0
```

`host authorize` performs native-store readiness first, then OAuth discovery,
Authorization Code with PKCE, browser login, token exchange, and card-bound
session storage. Reload permission is demand-driven and request-bound. The
runtime resolves the live delegated card immediately before the operation.

```bash
connection-hub host disconnect
```

Disconnect revokes the OAuth grant and its delegated card before removing
local custody.

## Manage And Export Secrets

KDCube owns the backend-neutral management protocol and the canonical
`kdcube secrets metadata|get|set|delete|export` commands. Connection Hub's
`secrets host` commands wrap the same KDCube library, select the configured
host, and supply the OAuth session held in the native credential store.

An authorized operator or automation can manage exact secrets through its live
Connection Hub Card:

```bash
connection-hub secrets host metadata platform.services.brave.api_key --scope platform
connection-hub secrets host set platform.services.brave.api_key --scope platform
connection-hub secrets host get platform.services.brave.api_key --scope platform \
  --output ./brave-api-key
connection-hub secrets host delete platform.services.brave.api_key --scope platform
```

`set` uses a hidden prompt by default. `get` writes a `0600` local file on
POSIX, uses the selected parent directory's ACL on Windows, and reports
disclosure metadata without printing the value. Connection Hub checks the
exact target and operation on every call. The CLI's Card gains each exact
key through its consent, or in advance in the Card editor (**Once** or **Every
time**; see the step-by-step below). Before requesting disclosure, `get` rejects a
missing parent, an existing destination without `--replace`, or a non-file
replacement target. The atomic writer repeats these checks when publishing so
a filesystem race cannot silently clobber another path.

### Set a secret on a KDCube deployment, step by step

This is the whole path for a person with no delegated bearer, on a fresh
machine.

**Before you start.** The deployment's Connection Hub must register the
management secret resources for admission: the `kdcube-management` service
under `connections.delegated_credentials.admission.services` lists
`urn:kdcube:management:secret:*:*:*:*:*`. That is the default in the Connection
Hub descriptor template. Your platform account must be allowed to approve
management grants; a deployment administrator is.

1. **Run the CLI.** Once the release is published, install it from PyPI:

   ```bash
   uv tool install connection-hub-cli
   connection-hub --version
   ```

   This flow uses `connection-hub-cli` 2026.10.25.0200 or later, which installs
   `kdcube-cli` 2026.10.25.0130 or later and the App Ecosystem packages at
   2026.10.25.0100 or later. The deployment must also run the Connection Hub
   app source from that App Ecosystem release: the hosted app supplies the
   authorization-code exchange fix. Check the CLI version before continuing.
   To use an unpublished revision, run both CLIs from source at the revisions
   your deployment runs:

   ```bash
   CH() { uvx \
     --from "git+https://github.com/elenaviter/app-ecosystem@<app-ecosystem commit>#subdirectory=products/connection-hub/packages/connection-hub-cli" \
     --with "git+https://github.com/kdcube/kdcube@<kdcube commit>#subdirectory=app/ai-app/src/kdcube-ai-app/kdcube_cli" \
     connection-hub "$@"; }
   CH --version
   ```

   Take both packages from source, at the App Ecosystem and KDCube revisions
   your deployment runs: the secret consent is handled in `kdcube-cli`, whose
   source version must satisfy the Connection Hub CLI's dependency floor.
   From `connection-hub-cli` 2026.10.25.0200 on, that means a KDCube revision
   carrying `kdcube-cli` 2026.10.25.0130 or later. Every command below works the
   same with `connection-hub` or `CH`.

2. **Select the deployment by its public address.** Use the address the
   deployment's OAuth issuer is configured for. A local workdir or `localhost`
   URL is refused at authorization with `oauth_issuer_mismatch` when the
   deployment publishes a public issuer.

   ```bash
   connection-hub setup --endpoint https://<public address>
   ```

3. **Authorize.** Your browser opens the deployment's login; sign in and
   approve.

   ```bash
   connection-hub host authorize
   ```

   This gives the CLI a Card for the deployment resource only. Exact secrets
   are added to that Card one at a time, when you first use them.

4. **Set the secret.** Pass the value on standard input so it never appears on
   the command line or in your history:

   ```bash
   openssl rand -base64 48 | tr -d '\n' | connection-hub secrets host set <key> \
     --scope bundle --bundle-id <application id> --value-stdin
   ```

   The first request for a key the Card does not hold yet answers with a
   consent. The CLI opens Connection Hub's grant page for that exact secret and
   operation, and asks you, on your terminal, to press Enter once you have
   approved it. On the page choose **Once** (this request only) or **Every
   time** (this operation on this secret from now on), then press the button
   that reads **Allow `<operation>` once** (or **Allow `<operation>` always**
   after choosing **Every time**). Nothing else on the page needs to change.
   The CLI then retries the identical request and prints a
   receipt with no value. With `--no-open --no-wait`, it prints the consent
   instead (`recovery.authorization_url`); open it, approve, and rerun the same
   command with the same `--invocation-id`.

   Agents and automation always pass `--no-open --no-wait`: in a
   pseudo-terminal with no person at it, the CLI would otherwise wait at its
   prompt. They relay `recovery.authorization_url` to the person and rerun
   with the same `--invocation-id`.

5. **Check it.** Metadata is a separate operation, so it asks once more in the
   same way:

   ```bash
   connection-hub secrets host metadata <key> --scope bundle --bundle-id <application id>
   ```

   It reports that the secret exists and which provider holds it, never the
   value.

Secret operations use the Card's **Once** / **Every time** policy. A platform
administrator can also add an exact secret to the CLI's Card in advance, in the
Card editor (Connection Hub, Delegated by KDCube, the Connection Hub CLI Card):
add the secret with its operations and **Once** or **Every time**, then save.
This works for a Card entered through the deployment door, which is what
`host authorize` creates, and for a Card entered through one exact secret. The
editor adds exact secrets only on such a Card. A secret selector (a `*` scope or
key) is refused with `oauth_client_resource_unreachable`, and so is a secret on a
Card entered through any other door. The Card is the live authority, so a grant
applies to the next request without authorizing again. The full admin path,
with troubleshooting, is KDCube's
[KDCube Admin: Manage A Secret With The Connection Hub CLI](https://github.com/kdcube/kdcube/blob/main/app/ai-app/docs/service/secrets/manage-a-secret-with-connection-hub-cli-README.md).

Descriptor export is an administrator-performed path with independent authority:

```bash
connection-hub secrets host export \
  --platform-key platform.services.brave.api_key \
  --bundle-key connection-hub@1-0=connections.oauth_state_secret \
  --user-key USER_ID~connection-hub@1-0=provider.refresh_token \
  --output-directory ./kdcube-secret-export
```

Export the complete provider inventory, including every user value:

```bash
connection-hub secrets host export \
  --all \
  --output-directory ./kdcube-secret-export-all
```

The command starts a PKCE-bound loopback callback and opens the KDCube approval
page. When no platform browser session exists, KDCube redirects through the
identity provider configured by that deployment. The signed-in platform
administrator sees the exact deployment, callback, digest, and frozen key
counts or selected key list, then chooses `Export once`. A whole-export start
response exposes only the count and digest; provider key names reach the CLI
only in the approved one-use exchange, where their frozen digest is verified
again. One authorization code permits one exchange for that manifest. The
flow leaves delegated Cards unchanged and stores no reusable export
credential.

The destination must be a new directory. The CLI validates a bounded response,
stages and flushes canonical `secrets.yaml` and `bundles.secrets.yaml`, then
atomically reserves the destination without replacing any existing path. It
moves only complete files into that owned directory and returns success after
both are durable; a handled failure removes its partial destination. POSIX
permissions are `0700` for the directory and `0600` for each file. Windows
inherits the ACL of the selected parent directory, so that parent must already
be private to the intended Windows user. The CLI prints only paths, counts,
request digest, and assurance evidence.

Repeat `--platform-key`, `--bundle-key BUNDLE_ID=KEY`, and
`--user-key USER_ID[~BUNDLE_ID]=KEY` for a selected manifest, or use `--all`
by itself. KDCube freezes whole provider inventory before approval. The same
command works with local and remote KDCube hosts and with file, Host Vault, and
cloud secret backends.

Restore or synchronize the literal pair through the selected host without
extracting its stored OAuth bearer:

```bash
connection-hub secrets host import \
  --input-directory ./kdcube-secret-export-all \
  --dry-run
connection-hub secrets host import \
  --input-directory ./kdcube-secret-export-all \
  --yes
```

Import is an upsert of every present platform, bundle, and user value. Omitted
keys remain unchanged; deletion is always an explicit `secrets host delete`.
The CLI stops on the first denied target and reports how many prior idempotent
upserts completed. Grant the denied exact key (**Once** or **Every time**) on
the same live CLI Card, through its consent or the Card editor, before
retrying. Secret
values remain absent from command arguments, output, state metadata, and logs.

The built-in `session_confirmation` assurance proves a current KDCube admin
browser session plus the exact click. Deployments configured for
`fresh_authentication` or `user_verification` fail closed until their
configured identity authority installs a verifier that can redirect through a
fresh IdP or WebAuthn/passkey challenge and return evidence bound to the exact
request digest and verification time. The transaction and CLI protocol remain
the same for those stronger verifiers.

## Diagnose And Recover

```bash
connection-hub status
connection-hub doctor --probe --json
connection-hub profile status coding-agent --probe
connection-hub client list --json
```

Diagnostics report the platform, exact backend, profile type, expiry state,
refresh readiness, client mode, and owned-entry state. Credential values are
excluded from command arguments, environment variables, client configuration,
state files, output, and ordinary logs.

Profile probes preserve `mcp_authorization_rejected` when the exact governed
endpoint returns HTTP `401` or `403`, even when the MCP transport wraps that
response. Upstream response text is not returned. Connection and timeout errors
remain separate, allowing a caller to request new consent only after an actual
authorization rejection instead of rotating a healthy Card during an outage.

OAuth browser reconnect preserves the profile's caller Card:

```bash
connection-hub profile reconnect coding-agent
```

The command reuses the recorded OAuth client. It stores the new token only
after the endpoint proves that token and its `access_id` matches the recorded
Card. A different-Card grant is revoked while local profile and credential
state remain unchanged.

Use `connection-hub profile reconnect coding-agent --device` to preserve the
same Card without a browser or callback listener on the client host. Device
mode prints only the public verification URL and user code; tokens remain in
the native credential store.

Static credential replacement validates the candidate before switching:

```bash
connection-hub profile credential replace coding-agent
```

OAuth profile disconnection revokes server authority before local retirement:

```bash
connection-hub profile disconnect coding-agent
```

If local OAuth custody is missing, revoke the recorded `access_id` through the
Connection Hub owner interface before using the explicit local-abandonment
flags on `profile remove`.

Set `CONNECTION_HUB_STATE_DIR` when non-secret CLI metadata needs an isolated
root. Keep the real desktop user's `HOME` so its native store remains
discoverable.

## Python Composition

Domain host processes can use the same profile boundary directly. It lives in
the `connection-hub` package (`connection-hub[client]`), so a host does not
need this command line to use it:

```python
from connection_hub.caller.profile_connection import connect_profile_tools

async with connect_profile_tools(
    profile_name="coding-agent",
    profiles=profiles,
    credentials=credentials,
    oauth_sessions=oauth_sessions,
) as (remote_tools, _mcp_client):
    tools = await remote_tools.list_tools()
```

`connect_profile_tools()` uses the same protected OAuth profile session as the
stdio relay. The domain caller receives connected tools while refresh
credentials remain inside the profile service.

The complete command, security, client, recovery, and acceptance contracts are
documented in [Connect Desktop MCP Clients To Connection Hub](https://github.com/elenaviter/app-ecosystem/blob/main/docs/connection-hub/local-client-helper.md).
Real Windows Credential Manager, Linux Secret Service, Hermes, and OpenClaw
acceptance is recorded separately from mocked portable tests.

License: MIT. Source: https://github.com/elenaviter/app-ecosystem
