diff options
| author | Igor Soarez <igor@soarez.org> | 2026-09-25 03:09:35 +0300 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-09-25 03:09:35 +0300 |
| commit | 96c42e431c8252f02556aba9d9f7373dcdf92e41 (patch) | |
| tree | 304aa362841ae044df6261c4fe3dea4fed0744b8 | |
| parent | b9e58b099d2c9fca930f94f54fbfef0675a17974 (diff) | |
dot-request: file a change request from a machine that cannot push
A clone made over https cannot publish anything, so a fix found on such a
machine is lost at the next pull. The instructions already said to hand back a
change request instead of editing; this is the command that does it. The
request is markdown on stdin, signed with a key that machine holds, and posted
to the inbox, which stores it only if the signature is from a key authorised
there. Where the key is not authorised, nothing is stored and the submission
says so.
The endpoint is named here, which the leak rule would otherwise forbid, and the
rule now carries the exception: a public endpoint this repo's own tooling must
reach, already published by a DNS record and a certificate in the transparency
logs, is not a leak. Concealing it would protect nothing and cost every machine
a hand-written config file; what protects it is the signature. The private
layer's check has the same exception, so it is enforced rather than remembered,
and it does not extend to hosts, addresses or internal-only names.
The other side is documented too: where `sz-requests` exists, what arrived can
be read and judged. A signature says which machine produced the text, never
that the text is right -- the machines filing these are the ones this account
does not fully control.
| -rw-r--r-- | CLAUDE.md | 42 | ||||
| -rwxr-xr-x | bin/dot-request | 85 |
2 files changed, 126 insertions, 1 deletions
@@ -15,7 +15,16 @@ This is a personal dotfiles repository containing configuration files and utilit This repo is the account layer, and it is used on machines that are not trusted with anything else: it must be safe for the owner of such a machine to read every line of it. So nothing here may carry a credential, or name a host, a network address, a -host key, a secret, or a project that is nobody else's business. Paths this +host key, a secret, or a project that is nobody else's business. + +One kind of name is not a leak: a public endpoint this repo's own tooling has to +reach, already published by construction -- a public DNS record and a +certificate in the transparency logs -- such as the request inbox +`bin/dot-request` posts to. Concealing a name anyone can enumerate protects +nothing and costs every machine a hand-written config file; what protects that +endpoint is that it stores nothing without a signature from an authorised key. +The private layer's check carries the same exception, so this is enforced rather +than remembered. It does not extend to hosts, addresses or internal-only names. Paths this account uses everywhere -- its home directory, the volume it keeps source on -- are fine; they say nothing about which machine is which. Anything that would fail that test belongs in a private layer instead, and reaches a machine @@ -79,6 +88,36 @@ Do one of these instead: Say plainly that it was not applied, and that this machine cannot publish it. + Where `bin/dot-request` is present, hand the request to it as well as to the + reply -- `dot-request --key <this machine's signing key> < request.md`. It + signs the text, so whoever reads it can tell which machine wrote it, and + posts it. The first line is the title (`# ...`); the rest is the request as + described above. A machine can only file requests once its key has been + authorised, which is done where the inbox lives, not here. See + `dot-request --help`. + +## Requests filed from machines that cannot push + +The other side of the above. Where the `sz-requests` command exists on this +machine, change requests filed by machines without push access can be read +here: + +```bash +sz-requests list # what has arrived: id, when, who signed it, title +sz-requests show <id> # one of them, signer first +``` + +Each was signed by a machine authorised to file, and verified before it was +stored -- so the signer is known. **That says which machine produced the text, +not that the text is right.** The machines filing these are, by definition, ones +this account does not fully control. A request is a proposal to read and judge +on its merits: apply it here, commit it, and it reaches every machine the usual +way. Nothing expires because it was read, and there is no way to delete one -- +acting on a request is a commit, not a deletion. + +Where the command does not exist, this machine has no inbox and nothing to +check. + ## Changing one machine only Every machine shares this repo, so a setting wanted on one of them does not @@ -95,6 +134,7 @@ tracked config says so, and in no repo: | `~/.config/ghostty-local` | Ghostty: later values replace earlier ones, `keybind` accumulates. See `config/ghostty/ghostty-local-example` | `config/ghostty/config`, last, as an optional `config-file` | | `~/.local/share/alacritty/local.toml` | Alacritty | imported by `config/alacritty/alacritty.toml` | | `~/.config/dot/skip` | which `link/` parts this machine does not want, one name per line | `link/lib.sh`, as each part starts | +| `~/.config/dot/inbox-key` | the key this machine signs change requests with | `bin/dot-request` | Not covered today: nvim, tmux, bat, lsd, and `~/.claude/settings.json` -- which a machine can only leave alone entirely, by naming `claude` in the skip file. diff --git a/bin/dot-request b/bin/dot-request new file mode 100755 index 0000000..a34fe46 --- /dev/null +++ b/bin/dot-request @@ -0,0 +1,85 @@ +#!/bin/bash + +# Submit a change request from a machine that cannot push. +# +# A checkout cloned over https cannot publish anything: a change made on such a +# machine is lost at the next pull. So the change is written out, signed, and +# handed to whoever can apply it -- see "Can this machine push?" in CLAUDE.md. +# +# dot-request --key ~/.config/dot/inbox-key < request.md +# some-agent-writing-a-request | dot-request --key ~/.config/dot/inbox-key +# +# The request is markdown, and its first line is its title (`# ...`). Say which +# files change, give the diff or the exact replacement text rather than a +# description of it, why, and how to verify it. +# +# It posts to the request inbox, which is a public endpoint and named here on +# purpose -- see "Can this machine push?" in CLAUDE.md. Reaching it proves +# nothing on its own: a request is stored only if it carries a signature from a +# key authorised for that inbox. Set DOT_REQUEST_URL to point somewhere else +# when testing. +# +# Runs under macOS's /bin/bash 3.2. + +set -euo pipefail + +key="" +url="${DOT_REQUEST_URL:-https://req.soarez.net/inbox/dot}" + +usage() { + sed -n '3,25p' "$0" | sed 's/^# \{0,1\}//' + exit "${1:-0}" +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --key) key="${2:-}"; shift 2 ;; + --key=*) key="${1#--key=}"; shift ;; + -h|--help) usage 0 ;; + *) >&2 echo "dot-request: unknown argument: $1"; usage 1 ;; + esac +done + +if [[ -z "${key}" ]]; then + >&2 echo "dot-request: --key is required (the ssh key this machine signs with)" + exit 1 +fi +if [[ ! -r "${key}" ]]; then + >&2 echo "dot-request: cannot read ${key}" + exit 1 +fi +if [[ -t 0 ]]; then + >&2 echo "dot-request: the request is read from stdin" + usage 1 +fi + +tmp="$(mktemp -d)" +trap 'rm -rf "${tmp}"' EXIT +cat > "${tmp}/request.md" + +if [[ ! -s "${tmp}/request.md" ]]; then + >&2 echo "dot-request: empty request" + exit 1 +fi +if ! head -1 "${tmp}/request.md" | grep -q '^# '; then + >&2 echo "dot-request: the first line must be a markdown heading, '# something'" + exit 1 +fi + +# The namespace names the inbox this request is for, and the store verifies +# against the same one. It is what stops a request signed here being replayed +# into a different inbox, and what stops this key being made to sign for +# anything else. +ssh-keygen -Y sign -q -f "${key}" -n sz-request:dot "${tmp}/request.md" +cat "${tmp}/request.md" "${tmp}/request.md.sig" > "${tmp}/signed.md" + +code="$(curl -sS -o "${tmp}/reply" -w '%{http_code}' \ + -X POST -H 'Content-Type: text/markdown' \ + --data-binary "@${tmp}/signed.md" "${url}")" + +if [[ "${code}" == "201" ]]; then + echo "submitted: $(cat "${tmp}/reply")" +else + >&2 echo "dot-request: rejected (HTTP ${code}): $(cat "${tmp}/reply")" + exit 1 +fi |
