Project Architecture

This page outlines the internal software design and architecture of the SKM codebase. It is primarily intended for developers wishing to audit the security of the tool, understand its execution flow, or contribute to the Open Source project.

The Core Principle: Zero Data Loss

SKM is designed around one absolute rule: Never destroy a user's original SSH keys.

When a user initializes SKM (skm init), the tool does not delete the contents of ~/.ssh. Instead, it executes a carefully choreographed migration:

  1. It creates an isolated directory at ~/.skm/default/.
  2. It physically moves the id_rsa/id_ed25519 files from ~/.ssh/ to the new vault.
  3. It immediately generates symbolic links back in ~/.ssh/ pointing to the vault.

Because the active ~/.ssh directory relies entirely on symlinks, SKM can swap identities in milliseconds simply by breaking and recreating those symlinks. The underlying private keys remain untouched in their respective ~/.skm/<alias> folders.

Codebase Organization

SKM is written entirely in Go (Golang) and utilizes urfave/cli.v1 to handle command-line routing and flag parsing.

cmd/skm/ (The Entrypoint)

This package handles the user-facing CLI layer.

  • main.go: The root execution point. It defines global flags, parses crucial environment variables (like SKM_STORE_PATH), and mounts the CLI application.
  • commands.go: Defines the command schema (aliases, usage descriptions, and flags). It maps CLI commands directly to functions in the internal/actions package.
  • Makefile: Contains compilation scripts, handling cross-platform builds and binary packaging for releases.

internal/actions/ (Business Logic)

This directory holds the logic for every executable command.

  • actions.go: Contains the critical Initialize sequence, ensuring safe data migration.
  • use.go: Implements the symlink swapping logic. Notably, it leverages the manifoldco/promptui library to render the interactive, scrollable alias selection menu if no arguments are provided.
  • Command-specific files (create.go, delete.go, rename.go, etc.) execute the business logic, heavily relying on the utils package to interact with the filesystem and OS binaries.

internal/models/ (Data Structures & Extensibility)

  • Environment: A struct encapsulating dynamic operational paths (StorePath, SSHPath, ResticPath). By passing this struct through the application rather than hardcoding paths, the entire codebase becomes highly testable. Unit tests can construct an Environment pointing to temporary /tmp directories, ensuring tests never overwrite a developer's real keys.
  • KeyType & registry.go: SKM uses a registry pattern to define supported SSH key algorithms. The SupportedKeyTypes map currently handles rsa and ed25519.
  • Extensibility: If a contributor wants to add support for ECDSA keys, they simply need to append a new KeyType definition into registry.go with the appropriate KeyBaseName (e.g., id_ecdsa). The rest of the application will dynamically adapt.

internal/utils/ (System Interaction)

  • Execute: A robust wrapper around Go's os/exec.Command. SKM does not reinvent cryptography; it delegates generation and caching to the OS's native OpenSSH binaries (ssh-keygen, ssh-add).
  • CreateLink / ClearKey: The core symlink engine. CreateLink handles relative pathing calculations to ensure symlinks remain valid even if the parent directories are moved.
  • RunHook: Inspects the alias directory for an executable file named hook and runs it as a child process during a key swap.

pkg/lib/ (External Integrations)

  • restic.go: Encapsulates all Restic logic. It handles the JSON configuration parsing (restic.json), repository initialization (restic init), and the execution of backup/restore subprocesses while passing the required password file flags securely.