Data model and rendering#

Connection profiles#

A connection profile contains one case-insensitively unique alias, hostname, port, username, optional credential ID, ordered ProxyJump aliases, project or scenario tags, notes, source metadata, and the most recent test result. Tags are case-insensitively deduplicated; one profile may belong to multiple contexts. Create another alias when the same endpoint needs another account.

Context names also have a small registry in the settings table. This keeps an empty context available after its last host is unassigned. Creating or updating a tagged connection registers any new names. Rename, deletion, and bulk host assignment rewrite the registry and all affected connection tags in one SQLite transaction, so an invalid host assignment rolls the complete change back.

Connection status is a timestamped observation, not a persistent online flag. Successful SFTP browsing and explicit host SSH tests both update it. The UI shows saved status and observation times; it does not probe every stored host in the background. Independent credential probes have separate history and never change a host's health record.

SQLite schema 6 adds nullable facts, last_success_at, last_resolved_ip, host_key, and geo fields. Successful diagnostics retain sanitized system facts with their observed_at, os_like, and closed os_logo key. Failed checks replace the latest result but keep previous successful facts and last_success_at. A locally resolved IP comes from an explicit check and may differ from the peer address through ProxyJump. Host-key state is verified, unknown, changed, or revoked; stored fingerprints do not replace OpenSSH verification. Manual geography stores an ISO country code and optional city. address_scope is derived from the literal hostname without DNS. Inventory and detail reads perform no DNS, SSH, or vault operations.

Changing hostname or port clears observations and test history for the old endpoint, and delayed results from that endpoint cannot repopulate them. User-entered geography, notes, tags, paths, and skill bindings remain. Schema initialization and migrations, partial updates, and route validation hold a SQLite immediate write transaction so separate processes cannot apply stale read/merge/write snapshots or race a schema upgrade.

Reject whitespace, shell metacharacters, and OpenSSH wildcard characters in managed aliases. Reject invalid ports, self-jumps, and managed ProxyJump cycles. When an alias changes, validate the complete graph with dependent references rewritten before committing. Creating or renaming a profile into a previously external hop also validates the resulting managed graph.

Credential profiles#

A credential contains a unique label and one authentication kind:

  • password: vault-backed password.
  • key: absolute private-key path plus an optional vault-backed passphrase.
  • agent: OpenSSH defaults or ssh-agent; no stored secret.

Credentials may be reused by multiple connection profiles. Default deletion refuses references. A separate explicit preview/detach-delete operation checks the expected revision/timestamp and exact host ID set, then detaches references, deletes the credential and records cleanup work in one transaction. Never delete the referenced key file.

Schema 7 adds an integer credential revision, nullable cached key_metadata, credential_tests and credential_deletions. Earlier schemas migrate in order without discarding host observations, existing credentials or bindings. Public key metadata includes the observed format, algorithm, fingerprint, bits, encryption and permissions assessment; encrypted keys can have nullable public fields until validated. Private-key bytes and passwords/passphrases are never stored in SQLite. Read-only credential summaries use this cache, not a fresh file/vault lookup.

Credential updates increment the revision. Test results record that revision and are bounded to the latest 50 completed results; the current last_test ignores older revisions while their history remains available. Credential deletion cascades its test history. Tests temporarily override the target host's credential without modifying its saved binding or health record.

The deletion journal survives removal of its credential. It stores an operation ID, sanitized credential metadata, detached host references, remaining vault slots/config cleanup, attempt count and a sanitized error code. Secret values are never journaled. A completed DB deletion with failed cleanup remains committed/pending rather than appearing fully erased. Retry is idempotent and does not re-detach hosts or remove the user's key file. Partial credential creation failures use the same journal if compensating vault cleanup fails.

Per-credential cross-process locks serialize vault mutation, tests, reveal, binding changes and deletion. Private-key saves verify the same bounded file snapshot before database commit; a changed key aborts and restores the prior vault value. Browser key revision tokens and directory scopes are ephemeral, session-bound metadata, never persisted credential material.

Host-bound skills#

A registered skill contains a case-insensitively unique name, a normalized absolute path to its local SKILL.md, the frontmatter description, and created and updated timestamps. The registry stores no skill body. The path may be registered from either a skill root directory or the SKILL.md itself.

Skills and connection profiles have a many-to-many relationship. One skill may apply to several related nodes, and one host may use several complementary skills. Relationships reference stable server IDs, so renaming an alias keeps its bindings and separate accounts on the same endpoint can have different skills. Registration with repeated --server aliases and multi-host binding changes are atomic: an unknown host or invalid association rolls back the whole mutation. Block registry deletion while hosts are bound. Once detached, removing the registry entry never deletes the local skill directory.

Resolve host-specific context only after the target aliases are known. The result includes a per-host view and a deduplicated skill view with an applies_to alias list, so a multi-host task does not leak one environment's instructions into another. Readiness is computed from the current local file: ready, missing, invalid, or name_mismatch. A non-ready association fails resolution instead of falling back to another same-name path.

Discovery scans standard local agent skill roots and SSM_SKILLS_DIRS without changing the registry. Same-name candidates at different paths are reported as conflicts; the registry's case-insensitive uniqueness constraint prevents an ambiguous registration.

Saved working directories#

Each record belongs to exactly one connection profile and holds a label, a remote path, and an optional note. The relationship is one-to-many rather than the many-to-many shape skills use, because a path describes one machine's filesystem: /srv/app on web1 and /srv/app on web2 are two records, and deleting a host deletes its own.

Labels are unique per host under Unicode case folding, so app and APP are the same directory, and either spelling resolves it. Paths are unique per host as well, which stops the same directory being saved twice under two names. Both uniqueness rules are enforced by the database, not by the caller.

Every record carries a last-used timestamp that listing sorts on, so the directories someone actually works in rise to the top while untouched ones stay alphabetical. Replacing a host's whole set preserves those timestamps by path, so renaming a directory in the UI does not reset what is known about it.

Saved directories are local metadata and nothing more. They record where a person chose to work; they are not a cache of the remote filesystem, are never verified against it, and their presence is not evidence that a path still exists.

SSH config#

Render managed entries first, then include each imported host's selected source config after its explicit managed values. Hosts imported from a custom source are excluded from the original config's fallback so additive IdentityFile directives from unrelated configs do not mix. External aliases remain available through fallback Includes. Format absolute paths for the actual SSH client; Git/MSYS uses its own bounded cygpath conversion on Windows, while native OpenSSH uses forward slashes. Reject a conversion failure or Include cycle before applying an import. Write atomically with user-only permissions. Hold the database write lock across the saved-profile snapshot and atomic replacement so a concurrent render cannot publish an older configuration last.

Import#

Follow Include files, enumerate literal Host aliases, and resolve each alias with ssh -G. Copy imports into the database; do not keep them synchronized automatically. Preview additions, unchanged entries, and conflicts. Skip conflicts unless overwrite is explicit.

Store each applied host's canonical source path under its stable ID in the ssh_config_import_sources setting, in the same transaction as a create or update. Renaming retains the source; deleting removes its association. The existing settings table needs no schema migration. Imported connection fields are snapshots, while authentication directives continue to be read by OpenSSH from the source config. Warnings expose this distinction and public IdentityFile paths without reading keys or creating credentials. Overwrite merges only imported connection fields with the latest record, preserving credential bindings, notes and other local metadata.

Edit this page on GitHub