Skip to main content

keywharf

keywharf is a Python 3.11+ CLI for selecting SSH hosts from a host repo into a local desired state, then materializing only manager-owned SSH artifacts.

It manages only:

  • one explicit local state file
  • one managed SSH config fragment
  • one managed key directory

It does not take over the user's whole ~/.ssh/config. Only install-include may minimally append one Include block to the main SSH config.

Create one named workspace:

keywharf init demo --directory ~

If you already have a host repo remote URL:

# edit ~/demo/config.json and set host_repo_remote_url
keywharf --workspace ~/demo repo sync
keywharf --workspace ~/demo repo host list
keywharf --workspace ~/demo repo host show demo

repo sync uses GitPython as an object interface to the required system Git executable. The configured origin URL must exactly match host_repo_remote_url; synchronization clones or pulls fast-forward-only and never commits, pushes, stashes, resets, cleans, or rewrites history. System credential helpers, the SSH agent, SSH configuration, and known_hosts remain authoritative.

High-frequency read-only convenience views are also available:

keywharf --workspace ~/demo list repo
keywharf --workspace ~/demo show repo demo
keywharf --workspace ~/demo list local
keywharf --workspace ~/demo show local demo

If you are starting from scratch:

keywharf --workspace ~/demo repo init
keywharf --workspace ~/demo repo host add demo --comment "demo host"
keywharf --workspace ~/demo repo host endpoint add demo public --hostname demo.example.com --comment "public endpoint"
keywharf --workspace ~/demo repo host auth add demo home --user fox --identity-file keys/id_demo --comment "home key"

keywharf init creates ~/demo/repo as the workspace's one host repo directory, but it starts empty. repo init writes the host repo skeleton there. repo host add creates a host shell only; endpoint and authentication options are added separately by stable name. If you want ~/demo/repo to become a real git repository, run git init, git remote add, and git push there yourself.

Then continue with normal selection and apply flow:

keywharf --workspace ~/demo select demo
# or select explicitly by stable name:
# keywharf --workspace ~/demo select demo --endpoint public --auth home
keywharf --workspace ~/demo validate
keywharf --workspace ~/demo render
keywharf --workspace ~/demo apply
keywharf --workspace ~/demo install-include

select still accepts explicit --endpoint and --auth stable names. If one side has a single candidate, it is selected automatically. If one side has multiple candidates, select prompts in an interactive terminal and fails fast in noninteractive environments until you pass the stable name explicitly. Local state keeps name-based selectors; singleton selections may leave endpoint_name or authentication_name as null.

If the manager config lives outside the default workspace root, use --config <path> instead of --workspace.

Canonical Identity and SSH Names

One ServerName identifies a connection definition. Optional shared Aliases declare additional literal SSH names; each client explicitly selects a non-empty subset with repeated --name:

keywharf repo host add demo-node-01 --alias dev --alias work
# Add the endpoint and authentication separately.
keywharf select demo-node-01 --endpoint direct --auth developer --name demo-node-01 --name dev
keywharf select demo-node-01 --endpoint direct --auth developer --name dev

All enabled names share one connection block and canonical key copy. Management commands always use ServerName. Without --name, first selection defaults to canonical-only and reselection preserves names. A terminal offers a numbered multi-selection with that default when aliases exist. Adding an alias never enables it automatically. Removing an enabled alias requires explicit replacement; renaming a canonical identifier requires new client selections even if the old name remains an alias.

State is now v2. V1 loads in memory with canonical-only names; only successful select/deselect writes upgrade retained entries. apply does not rewrite state. Upgrade every client/editor before introducing shared aliases or private renames. Downgrade needs coordinated original-file restoration, not a version-number edit. See configuration and CLI details.

Ownership Boundary

keywharf manages:

  • state_path
  • managed_config_path
  • managed_keys_dir

keywharf does not manage:

  • unrelated Host entries in the main SSH config
  • Match blocks
  • other Include lines
  • user comments and ordering in the main SSH config

Workspace Discovery

Workspace resolution is explicit and predictable:

  1. --workspace
  2. KEYWHARF_WORKSPACE
  3. auto-search pwd, each ancestor, then ~
  4. for each base directory: scan one level of child directories first, then the base directory itself
  5. the first directory containing KEYWHARF_WORKSPACE wins
  6. fail fast with the checked candidate paths listed

keywharf init <workspace_name> creates the marker, config.json, state/state.json, an empty repo/ directory, and small workspace text files from package resources. It does not touch ~/.ssh and does not initialize a git repo for you.

Formal Config And Templates

Manager config is a formal runtime config:

  • defaults come from pkg://keywharf/config_defaults/manager.json
  • file or mapping input is override only
  • defaults and overrides are deep-merged before Pydantic v2 validation
  • runtime path resolution is separate from raw config loading

Resource roles are intentionally split:

  • config_defaults/*.json: formal defaults for manager config
  • templates/*.json: structured starter data such as the empty state file
  • templates/*.j2: human-facing text templates such as workspace README.md, workspace .gitignore, and the include block text

Host Repo CRUD

repo init bootstraps a local-first host repo skeleton:

  • empty config.json
  • keys/
  • .gitignore

It writes those files into the workspace's one host repo directory, %{WORKSPACE}/repo. It does not run git init or create .git.

repo host edits only the host repo config.json:

  • repo host list
  • repo host show
  • repo host add
  • repo host update
  • repo host remove
  • repo host endpoint list
  • repo host endpoint show
  • repo host endpoint add
  • repo host endpoint update
  • repo host endpoint remove
  • repo host auth list
  • repo host auth show
  • repo host auth add
  • repo host auth update
  • repo host auth remove

These commands do not commit, push, run git init, or mutate git metadata. They perform structured JSON reads/writes, preserve array order, and revalidate the resulting host set before writing.

Design rules:

  • repo host add creates a host shell and may set its comment and aliases
  • repo host endpoint ... manages named endpoint options with HostName, optional Port, and optional comment
  • repo host auth ... manages named authentication options with optional User, optional IdentityFile, and optional comment
  • select writes name-based endpoint/authentication selectors into local state; singleton selections may leave those fields null
  • validate scans the whole host repo and reports every host shell that is missing endpoint options, authentication options, or both
  • render and apply only require the hosts selected in local state to be complete

ExtraConfig is preserved and rendered, but it is not exposed as a CLI editor yet.

Read-only convenience facade (canonical paths stay primary):

  • list repo -> repo host list
  • show repo <server> -> repo host show <server>
  • list local -> local list
  • show local <server> -> local show <server>

Scope boundary:

  • convenience targets are only repo (host definitions) and local
  • convenience commands are read-only only
  • there is no convenience facade for repo host endpoint ..., repo host auth ..., or any write operations

--sudo

Mutating commands support --sudo:

  • init
  • repo init
  • repo sync
  • select
  • deselect
  • apply
  • install-include
  • repo host add
  • repo host update
  • repo host remove
  • repo host endpoint add
  • repo host endpoint update
  • repo host endpoint remove
  • repo host auth add
  • repo host auth update
  • repo host auth remove

Privilege handling is centralized:

  • normal writable paths run without sudo
  • unwritable paths fail fast with concrete path-based reasons
  • --sudo re-execs the full command through sudo

Installation

python3.11 -m venv .venv
. .venv/bin/activate
python -m pip install -e '.[dev]'
pytest

Runtime requirements:

  • Python 3.11+
  • system git
  • GitPython 3.1.59 or newer within the supported 3.x series (installed with Keywharf)

Documentation

Metadata

Release files for keywharf 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for keywharf 1.1.0
File Size Uploaded Provenance
keywharf-1.1.0.tar.gz 81.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for keywharf 1.1.0
File Interpreter ABI Platform
keywharf-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 163.7 kB

Release files / keywharf-1.1.0.tar.gz

Download URL keywharf-1.1.0.tar.gz
Size 81.3 kB
Tags Source
SHA-256 checksum
How to use checksums
fabfd3eb27441ee4959bf763dec82e20d465eb51fd0e4abf2e06bbe1119e459d
BLAKE2b-256 checksum
How to use checksums
5c15235893258d11ad55141daef873d7622ea94d6ce362cf5550875c8f9f0eb2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release files / keywharf-1.1.0-py3-none-any.whl

Download URL keywharf-1.1.0-py3-none-any.whl
Size 82.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
22a1b3fde6b42789e4b1594f8277adde7f1225d9a3515aa26cf51359af6a62a4
BLAKE2b-256 checksum
How to use checksums
93e4321bfc2cc3657b8b8155c263f5d0a66c30af2da5ae9e1975585ebc5bfc72
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Security for keywharf 1.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for keywharf-1.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page