From 442c3b7cf5a439ddc5e43502556ab8c75d802ed7 Mon Sep 17 00:00:00 2001 From: Fredrik Johansson Date: Sun, 12 Jul 2026 21:21:55 +0200 Subject: [PATCH] Add fleet setup template and a one-shot project bootstrap script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SETUP.local.md.example is a fill-in-the-blanks runbook for rolling keep out across real machines and projects (gitignored once copied to SETUP.local.md — real vault keys/recipient ids/hostnames are fleet topology, not something to commit). bootstrap-project.sh wraps push + grant --read-only for one or more recipients into a single call for onboarding a new project vault. Co-Authored-By: Claude Sonnet 5 --- .gitignore | 5 +++ SETUP.local.md.example | 80 ++++++++++++++++++++++++++++++++++++ scripts/bootstrap-project.sh | 31 ++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 SETUP.local.md.example create mode 100755 scripts/bootstrap-project.sh diff --git a/.gitignore b/.gitignore index 25d66ac..ee2b291 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,8 @@ node_modules/ dist/ .env data/ + +# Personal rollout notes — real vault keys, recipient ids, VPS hostnames. +# Not secrets themselves, but fleet topology that doesn't belong in a +# public repo. See SETUP.local.md.example for the template. +SETUP.local.md diff --git a/SETUP.local.md.example b/SETUP.local.md.example new file mode 100644 index 0000000..c19fb42 --- /dev/null +++ b/SETUP.local.md.example @@ -0,0 +1,80 @@ +# keep fleet setup — personal notes (copy to SETUP.local.md, gitignored) + +Fill this in as you actually run the commands. It's your own record of +what recipient id belongs to which machine and which vault backs which +project — none of this is secret by itself (a recipient id or a vault +key doesn't grant access without the matching private key), but it's +real fleet topology that doesn't belong in a public repo. + +## 0. Server + +- [ ] Deployed at: `___________________` (e.g. https://keep.example.internal) +- [ ] `ADMIN_PASSWORD` stored in: `___________________` (password manager entry, not here) +- [ ] `KEEP_SERVER_URL` exported in shell profile on every machine below + +## 1. Identities + +One row per machine. `keep identity init` prints the public key; an +admin runs `keep recipient add`, which prints back a recipient id; that +id gets recorded locally with `keep identity set-id `. + +| Machine | Recipient id | Grant type (per vault, see §3) | +|---|---|---| +| this laptop | `___________________` | rw on everything it pushes | +| vps-1 (`___________________` hostname) | `___________________` | r- (read-only) | +| vps-2 | `___________________` | r- (read-only) | + +Commands, run once per machine: + +```bash +keep identity init # on the machine itself +KEEP_ADMIN_PASSWORD=... keep recipient add --label "" --pubkey # from an admin machine +keep identity set-id # back on the machine itself +``` + +## 2. Projects → vaults + +One vault per project+environment. Vault key is a free-form string — +it does NOT need to match the repo name or the deploy directory name +(see IMPLEMENTATION.md — these are three independent strings in this +fleet on purpose). + +| Project | Deploy dir | Vault key | `.keep-vault` written? | +|---|---|---|---| +| myapp | `/opt/docker/myapp` | `myapp/production` | [ ] | +| otherapp | `/opt/docker/otherapp` | `otherapp/production` | [ ] | + +Bootstrap a project: + +```bash +keep push myapp/production --file .env.production # from the laptop, becomes rw grantee +keep grant myapp/production --read-only +echo "myapp/production" > /opt/docker/myapp/.keep-vault # on vps-1, next to its docker-compose.yml +``` + +Or use `scripts/bootstrap-project.sh` (see below) to do the push+grant +step in one call for multiple VPS recipients at once. + +## 3. CI announce (optional, per project) + +Only needed if you want `keep watch` on a VPS to skip waiting for the +next scheduled sweep. Add to that project's own +`.gitea/workflows/docker.yml`, after the build-and-push step: + +```yaml +- name: announce to keep + continue-on-error: true + run: keep announce myapp/production --tag ${{ steps.meta.outputs.short_sha }} +``` + +The vault key here must be the literal string from §2's table — not +derived from the repo name. + +## 4. Sanity check + +```bash +KEEP_ADMIN_PASSWORD=... keep overview +``` + +Confirm every project vault shows up with exactly the grants you +expect — laptop `rw`, each deploying VPS `r-`, nothing else. diff --git a/scripts/bootstrap-project.sh b/scripts/bootstrap-project.sh new file mode 100755 index 0000000..0482d38 --- /dev/null +++ b/scripts/bootstrap-project.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Pushes a project's first env file and grants read-only access to one +# or more already-registered recipients (typically the VPS boxes that +# will deploy it), in one call. +# +# Usage: +# ./bootstrap-project.sh [recipient-id...] +# +# The pusher (this machine's identity) becomes the vault's first, +# read-write recipient automatically — see IMPLEMENTATION.md. Every +# recipient-id argument here is granted read-only; run `keep grant` +# by hand afterward for anyone who needs write access too. +set -euo pipefail + +vault="${1:?usage: bootstrap-project.sh [recipient-id...]}" +file="${2:?usage: bootstrap-project.sh [recipient-id...]}" +shift 2 + +if [ "$#" -eq 0 ]; then + echo "error: at least one recipient id is required" >&2 + exit 1 +fi + +keep push "$vault" --file "$file" + +for recipient in "$@"; do + keep grant "$vault" "$recipient" --read-only +done + +echo "" +echo "done. verify with: KEEP_ADMIN_PASSWORD=... keep overview"