diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..5e177c70 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,136 @@ +# Working on sesh + +For anyone changing sesh, person or coding agent. If you use sesh day to day +as well, the first thing is keeping the sesh you use apart from the one you're +working on. + +## Two seshes: the one in use, and the one being built + +- **The daily sesh** is an installed release (`brew install bashhack/sesh/sesh`, + so `/opt/homebrew/bin/sesh`). Its vault, config, agent and Touch ID key are in + the default places: `~/Library/Application Support/sesh/` (on Linux + `~/.local/share/sesh/`), `~/.config/sesh/config.toml`, and the agent's socket + under the user cache folder. +- **The dev sesh** is this checkout, run with `scripts/sesh-dev`. It builds the + working tree to `build/sesh-dev` and runs it on a vault, config and agent of + its own, in `SESH_DEV_DIR` (an absolute path; default + `~/.local/share/sesh-dev`): + + ```bash + scripts/sesh-dev add password github --username alice + scripts/sesh-dev ui + ``` + +Don't run a development build against the daily sesh's vault, config or agent. +The two would also replace each other's agent on every switch (an agent from +another build is replaced), so the daily sesh would keep asking to unlock. + +### For coding agents + +- **Use a throwaway folder for every run,** never the person's dev vault or the + daily one, and give the master password in the environment, with a small key + setting so it's quick: + + ```bash + export SESH_DEV_DIR=$(mktemp -d) SESH_MASTER_PASSWORD=throwaway-test-1 SESH_KDF_MEMORY=19MiB + printf 'pw\n' | scripts/sesh-dev add password demo --username me + scripts/sesh-dev list + ``` + + `mktemp -d` gives a short enough path: an agent socket's path can be at most + 103 characters on macOS. Delete the folder when you're done. +- **Don't touch:** the daily vault, `~/.config/sesh`, the running agent, + `~/.aws`, or the person's tmux sessions. Use a separate tmux server + (`tmux -L `) to drive `sesh ui`. +- **Don't copy:** these use the real clipboard: `sesh copy`, `--clip`, + `add --generate` and `generate --copy` (they copy the new password), + `history --clip`, and `sesh aws` while it signs in (it puts the MFA code + there). In `sesh ui`: Enter and Ctrl-Y, the copy actions, the password + generator, and saving a generated password. Read the code instead, or use + `sesh get`. +- **Don't run `sesh touchid enable` or `sesh browser install`** unless asked: + the first makes a key in the Mac's secure chip, tied to the person's + fingerprints; the second repoints their browsers (below). And don't accept + `sesh sync init`'s offer of an iCloud Drive folder: iCloud copies it to the + person's other computers. +- Stop every agent and process you start (`scripts/sesh-dev agent stop`), and + put a time limit on every test run, so nothing is left behind. + +### What the two still share + +- **The browser extension.** The browser starts whatever sesh the script in the + daily data folder (`browser-host`) names, without `scripts/sesh-dev`'s + settings, so it always runs on the daily vault and agent. + `scripts/sesh-dev browser …` refuses for that reason; to try the extension + against a dev build, try the build as the daily sesh (below). +- **The agent's log**, `agent.log` in the user cache folder: both agents write + to it. +- **The clipboard**, and the AWS CLI's own files: `scripts/sesh-dev aws` uses + the real `~/.aws`. For AWS work without AWS, see `scripts/README.md` (a mock + AWS CLI). +- **What the dev sesh starts** keeps its settings: in `sesh run`'s command, + `sesh aws`'s shell and the notes editor, `SESH_DB_PATH`, `SESH_AUTH_SOCK` + and `XDG_CONFIG_HOME` point at the dev folder, so a `sesh` run there uses + the dev vault, and other programs (git, an editor) look for their config in + it. The other way round, a `scripts/sesh-dev aws` run inside the daily + sesh's AWS shell refuses ("already in a sesh environment"), except for a + profile that signs in with `aws login` or SSO whose session has ended: the + dev build signs it in again. +- **`sesh init`:** it offers the default vault location, not the dev one; + `scripts/sesh-dev init` gives it the dev vault's. + +### Trying a dev build as the daily sesh + +Rarely, a change needs trying for real before it's released: + +```bash +go build -o ~/go/bin/sesh ./sesh/cmd/sesh # a folder on PATH after Homebrew's +brew unlink sesh && hash -r # now `sesh` is the dev build +~/go/bin/sesh browser install # the extension too, if needed +# … and back: +rm ~/go/bin/sesh && brew link sesh && hash -r +sesh browser install # the extension back to Homebrew's +``` + +While it's unlinked, a `browser-host` script naming Homebrew's sesh points at +nothing, so the extension only works after the `browser install` above. + +## Building and testing + +- Go 1.27+, and on macOS Xcode's command line tools: Touch ID and the screen + lock check are built with cgo. +- `go build ./...`, then test only the packages you changed while you work + (`go test ./internal/tui/`). CI runs everything, and the race detector in + three parts. It leaves Argon2id uninstrumented, for speed, which needs a Go + experiment turned off too, or it reports false races: + + ```bash + GOEXPERIMENT=nosizespecializedmalloc go test -race -gcflags='golang.org/x/crypto/argon2=-race=false' ./internal/... + ``` + + A plain `go test -race` works as well, more slowly. +- `make dev/setup/hooks` installs the pre-commit hook: goimports and + `go build ./...`, then go vet and golangci-lint on the packages a commit + touches. `make audit` tidies, formats, vets, lints and tests everything, and + rewrites files as it goes. +- `make agent-smoke` runs the agent end to end. + +## Changes + +- **One pull request per change**, each on its own branch (a git worktree per + branch keeps them apart). Each is reviewed before it's merged, and CI must + pass. +- **Commit messages are one line,** saying what the change does. +- **Stage files by name,** not `git add -A`: a checkout can hold notes and + scratch files that aren't meant for the repo. +- **Comments and docs say how things are,** not how they came to be. + `docs/USAGE_AND_CONFIGURATION.md`, `docs/SECURITY_MODEL.md` and + `docs/ARCHITECTURE.md` are checked against the code; change them with it. + +## Releasing + +Tag `vX.Y.Z` on `main` and push the tag. The release job tests it again, +builds, signs and notarizes the macOS binaries, publishes the GitHub release, +and updates the Homebrew formula in `bashhack/homebrew-sesh`. A tag with +`.github/release-notes/.md` is released with those notes; otherwise, with +the commits since the last tag. Then `brew upgrade sesh` updates the daily sesh. diff --git a/README.md b/README.md index 4d2c700b..6f75405d 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,21 @@ make coverage make audit ``` +### Developing while using sesh every day + +`scripts/sesh-dev` builds this checkout and runs it on a vault, config and +agent of its own (in `~/.local/share/sesh-dev`), so an installed sesh and its +vault are never touched: + +```bash +scripts/sesh-dev add password github --username alice +scripts/sesh-dev ui +``` + +[AGENTS.md](AGENTS.md) has the rest: what the two still share (the browser +extension, the clipboard), trying a dev build as your everyday sesh, and the +rules for coding agents working on sesh. + ## License MIT License - see [LICENSE](LICENSE) for details. diff --git a/scripts/README.md b/scripts/README.md index 9b96b134..a0f97015 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -2,19 +2,22 @@ This directory contains utility scripts to help with local development and testing of the `sesh` CLI tool. These scripts allow you to test AWS and TOTP flows without needing real AWS credentials or actual TOTP-enabled services. -**Use a scratch vault.** The flows below add entries to whatever vault sesh opens, so point it at a throwaway one first: - -```bash -export SESH_DB_PATH=/tmp/sesh-dev/passwords.db -export SESH_AUTH_SOCK=/tmp/sesh-dev/agent.sock # an agent of its own: the agent holds one vault's key at a time -``` - -Your config file (`~/.config/sesh/config.toml`) still applies. +**Use a scratch vault.** The flows below add entries to whatever vault sesh opens, so run them through `sesh-dev` (below), which gives a build of this checkout a vault, config and agent of its own: `scripts/sesh-dev add totp …` in place of `sesh add totp …`. **QR codes on screen** are read only on macOS (`--qr`). On Linux, save a screenshot of the code and give it with `--qr-image shot.png`. ## Scripts Overview +### 🧰 `sesh-dev` +**Purpose**: Build this checkout's sesh and run it on a vault, config and agent of its own (`SESH_DEV_DIR`, default `~/.local/share/sesh-dev`), apart from the sesh you use every day. + +```bash +scripts/sesh-dev ui +SESH_DEV_DIR=$(mktemp -d) SESH_MASTER_PASSWORD=throwaway scripts/sesh-dev list # a throwaway vault +``` + +See [AGENTS.md](../AGENTS.md) for what it keeps apart, and what it doesn't. + ### 🔐 `mock-totp-service.go` **Purpose**: Simulates an external TOTP service (like GitHub, Google, etc.) for testing the TOTP setup flow. diff --git a/scripts/sesh-dev b/scripts/sesh-dev new file mode 100755 index 00000000..5dd5553c --- /dev/null +++ b/scripts/sesh-dev @@ -0,0 +1,79 @@ +#!/bin/bash +# sesh-dev builds this checkout's sesh and runs it on a vault, config and +# agent of its own, so the sesh you use day to day (Homebrew's, say), its +# vault, its settings and its agent are never touched. +# +# scripts/sesh-dev add password github --username alice +# scripts/sesh-dev ui +# SESH_DEV_DIR=$(mktemp -d) scripts/sesh-dev ... # a throwaway one +# +# Everything lives in SESH_DEV_DIR (default ~/.local/share/sesh-dev), which +# must be an absolute path: the vault (passwords.db, with its backups and +# touchid.key beside it), the config (config/sesh/config.toml) and the +# agent's socket (agent.sock). The build is build/sesh-dev in this checkout, +# rebuilt on every run (Go's cache makes an unchanged one quick); a changed +# build replaces the agent. +set -euo pipefail + +repo=$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) +dir=${SESH_DEV_DIR:-$HOME/.local/share/sesh-dev} +case "$dir" in +/*) ;; +*) + # sesh ignores a config folder that isn't absolute, and would read the + # daily config instead. + echo "sesh-dev: SESH_DEV_DIR must be an absolute path, not $dir" >&2 + exit 2 + ;; +esac + +# The command: the first word after any --db-path the arguments start with +# (sesh takes it with any number of dashes). has_db_path: one is given anywhere. +cmd="" skip="" has_db_path="" +for a in "$@"; do + case "$a" in + -*db-path | -*db-path=*) has_db_path=1 ;; + esac +done +for a in "$@"; do + if [ -n "$skip" ]; then + skip="" + continue + fi + case "$a" in + -*db-path) skip=1 ;; + -*db-path=*) ;; + *) + cmd=$a + break + ;; + esac +done + +if [ "$cmd" = browser ]; then + # The browser starts sesh by the path browser install writes, without + # this script's settings: it would run the dev build on the daily vault + # and agent. + echo "sesh-dev: browser install would point your browsers at the dev build, run on your daily vault and agent. AGENTS.md (\"Trying a dev build as the daily sesh\") says how to try the extension against one." >&2 + exit 2 +fi + +mkdir -p "$dir" +chmod 700 "$dir" + +bin="$repo/build/sesh-dev" +(CDPATH='' cd -- "$repo" && go build -o "$bin" ./sesh/cmd/sesh) + +# Where the daily sesh's things are, from the shell, isn't inherited: a +# sync or backup folder set there would otherwise be this vault's too. +unset SESH_BACKUP_DIR SESH_SYNC_DIR SESH_SYNC_NAME +export SESH_DB_PATH="$dir/passwords.db" +export SESH_AUTH_SOCK="$dir/agent.sock" +export XDG_CONFIG_HOME="$dir/config" + +# sesh init offers the default vault location, not SESH_DB_PATH: given the +# dev vault's, it doesn't ask. +if [ "$cmd" = init ] && [ -z "$has_db_path" ]; then + set -- "$@" --db-path "$SESH_DB_PATH" +fi +exec "$bin" "$@"