From 1da0025fc5d4fa001124a5cf225a69a49a7d01bf Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Thu, 5 Nov 2026 10:09:23 +0100 Subject: content: add HardenedPass docs skeleton as drafts Placeholder pages for the new section in both languages, its i18n keys, the implementation plan and the theme with the section's layouts. Co-Authored-By: Claude Opus 5.5 --- .../plans/2026-11-05-hardenedpass-docs.md | 420 +++++++++++++++++++++ 1 file changed, 420 insertions(+) create mode 100644 docs/superpowers/plans/2026-11-05-hardenedpass-docs.md (limited to 'docs/superpowers/plans') diff --git a/docs/superpowers/plans/2026-11-05-hardenedpass-docs.md b/docs/superpowers/plans/2026-11-05-hardenedpass-docs.md new file mode 100644 index 0000000..b5d408c --- /dev/null +++ b/docs/superpowers/plans/2026-11-05-hardenedpass-docs.md @@ -0,0 +1,420 @@ +# HardenedPass Docs Section Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a bilingual, wiki-like HardenedPass documentation section to danix.xyz. + +**Architecture:** A new Hugo section `hardenedpass` with its own `list` and `single` layouts in the theme submodule. A sidebar shows the page tree and table of contents; on mobile both collapse into native `
`. Pages carry a unique `weight` that gives one reading order across subsections. + +**Tech Stack:** Hugo extended, Tailwind 3 (`npm run build`), TOML front matter, Go templates. + +**Spec:** `docs/superpowers/specs/2026-11-05-hardenedpass-docs-design.md` + +**Deviations from spec, found while planning:** +- `
` cannot be open on desktop and closed on mobile without JavaScript, so the tree renders twice, from a separate `docs-tree.html`. Prev/next is in `docs-pager.html`, because it sits in the content column. +- The menu entry moves to the last task. A `pageRef` to a draft page has no target, so adding it earlier could break the menu if `master` is deployed before the pages are published. + +**Rules for every task:** +- `SITE` is `~/Programming/GIT/danix.xyz-hacker-theme`, `THEME` is `$SITE/themes/danix-xyz-hacker`, `APP` is `~/Programming/GIT/HardenedPass`. +- Build into a scratch directory, never `public/`: `hugo -D --gc --minify -d "$OUT"`, with `OUT` a temporary directory. +- Never touch the `production` branch. Commit GPG signed. Do not commit `.claude/settings.local.json`. Push only when the maintainer asks. +- All new pages have `draft = true`. + +--- + +### Task 1: Skeleton pages + +**Files:** +- Create: `content/{en,it}/hardenedpass/_index.md` +- Create: `content/{en,it}/hardenedpass/{getting-started,using,reference}/_index.md` +- Create: `content/{en,it}/hardenedpass/getting-started/{concepts,install,create-store,connect}/index.md` +- Create: `content/{en,it}/hardenedpass/using/{entries,otp,sync,autofill}/index.md` +- Create: `content/{en,it}/hardenedpass/reference/{settings,security,troubleshooting}/index.md` + +- [ ] **Step 1: Generate the 30 files** + +```bash +cd "$SITE" +python3 - <<'EOF' +import pathlib +secs = { + "getting-started": (10, "Getting started", "Per iniziare", [ + ("concepts", 10, "Concepts", "Concetti"), ("install", 20, "Install", "Installazione"), + ("create-store", 30, "Create a store", "Crea un archivio"), ("connect", 40, "Connect the app", "Collega l'app")]), + "using": (20, "Using the app", "Usare l'app", [ + ("entries", 50, "Entries", "Voci"), ("otp", 60, "OTP codes", "Codici OTP"), + ("sync", 70, "Sync", "Sincronizzazione"), ("autofill", 80, "Autofill", "Autofill")]), + "reference": (30, "Reference", "Riferimento", [ + ("settings", 90, "Settings", "Impostazioni"), ("security", 100, "Security", "Sicurezza"), + ("troubleshooting", 110, "Troubleshooting", "Risoluzione dei problemi")]), +} +def put(path, title, weight=None): + w = f"weight = {weight}\n" if weight else "" + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(f'+++\ntitle = "{title}"\ndate = 2026-11-05T00:00:00Z\ndescription = ""\n{w}draft = true\n+++\n\nPlaceholder.\n') +for i, lang in enumerate(("en", "it")): + root = pathlib.Path("content") / lang / "hardenedpass" + put(root / "_index.md", "HardenedPass") + for slug, (w, en, it, pages) in secs.items(): + put(root / slug / "_index.md", (en, it)[i], w) + for p, pw, pen, pit in pages: + put(root / slug / p / "index.md", (pen, pit)[i], pw) +EOF +find content -path '*hardenedpass*' -name '*.md' | wc -l +``` +Expected: `30` + +No commit yet: Task 2 commits these with the layouts that render them. + +--- + +### Task 2: Layouts + +**Files (all in `THEME`):** +- Create: `layouts/hardenedpass/single.html`, `layouts/hardenedpass/list.html` +- Create: `layouts/partials/docs-nav.html`, `layouts/partials/docs-tree.html`, `layouts/partials/docs-pager.html` +- Modify: `layouts/index.json:2-3` + +**Files (in `SITE`):** +- Modify: `i18n/en.yaml`, `i18n/it.yaml` (append) + +- [ ] **Step 1: Write the failing check** + +```bash +cat > "$OUT_CHECK" <<'EOF' +#!/bin/bash +# Usage: check.sh . Fails if the docs section is not wired up. +set -e +d=$1 +c=$d/en/hardenedpass/getting-started/concepts/index.html +[ -f "$c" ] || c=$d/hardenedpass/getting-started/concepts/index.html +k=${c/concepts/connect} +u=${c/getting-started\/concepts/using\/entries} +grep -q 'rel=.\?next' "$c" +grep -Eq 'href="?[^" >]*/install/"?[^>]*rel=.?next' "$c" +grep -Eq 'href="?[^" >]*/using/entries/"?[^>]*rel=.?next' "$k" +grep -Eq 'href="?[^" >]*/connect/"?[^>]*rel=.?prev' "$u" +grep -q 'aria-current' "$c" +[ "$(grep -o 'id="\?TableOfContents' "$c" | wc -l)" = 0 ] +echo PASS +EOF +``` +`OUT_CHECK` is a scratch path outside the repo. Run it on a build: `hugo -D --gc --minify -d "$OUT" && bash "$OUT_CHECK" "$OUT"`. +Expected: exits non-zero, because the default layout has no `rel=next`. + +- [ ] **Step 2: Create `layouts/partials/docs-tree.html`** + +```go-html-template +{{ $current := . }} +{{ $root := site.GetPage "/hardenedpass" }} + +``` + +- [ ] **Step 3: Create `layouts/partials/docs-nav.html`** + +The tree and table of contents render twice, so the `id` Hugo puts on the table of contents is removed to keep ids unique. + +```go-html-template +{{ $toc := replace .TableOfContents ` id="TableOfContents"` "" | safeHTML }} +{{ $hasToc := gt (len .Fragments.Headings) 0 }} +
+
+ {{ i18n "docsMenu" }} +
{{ partial "docs-tree.html" . }}
+
+ {{ if $hasToc }} +
+ {{ i18n "docsOnThisPage" }} +
{{ $toc }}
+
+ {{ end }} +
+ +``` + +- [ ] **Step 4: Create `layouts/partials/docs-pager.html`** + +`Pages.Next` steps toward index 0, so the list is reversed to make "next" the following page in reading order. + +```go-html-template +{{ $pages := (site.GetPage "/hardenedpass").RegularPagesRecursive.ByWeight.Reverse }} +{{ $prev := $pages.Prev . }} +{{ $next := $pages.Next . }} +{{ if or $prev $next }} + +{{ end }} +``` + +- [ ] **Step 5: Create `layouts/hardenedpass/single.html`** + +```go-html-template +{{ define "main" }} +
+
+ {{ partial "docs-nav.html" . }} +
+ {{ partial "breadcrumb.html" . }} +

{{ .Title }}

+
+ {{ .Content }} +
+ {{ partial "docs-pager.html" . }} +
+
+
+{{ end }} +``` + +- [ ] **Step 6: Create `layouts/hardenedpass/list.html`** + +```go-html-template +{{ define "main" }} +
+
+ {{ partial "docs-nav.html" . }} +
+ {{ partial "breadcrumb.html" . }} +

{{ .Title }}

+ {{ with .Content }} +
{{ . }}
+ {{ end }} +
    + {{ range .Pages.ByWeight }} +
  • + {{ .Title }} + {{ with .Description }}

    {{ . }}

    {{ end }} +
  • + {{ end }} +
+
+
+
+{{ end }} +``` + +- [ ] **Step 7: Add the section to `layouts/index.json`** + +Replace lines 2-3 with: + +```go-html-template +{{- $projects := where .Site.RegularPages "Section" "slackware" -}} +{{- $docs := where .Site.RegularPages "Section" "hardenedpass" -}} +{{- $articles = ($articles | append $projects | append $docs).ByDate.Reverse -}} +``` + +- [ ] **Step 8: Add i18n keys** + +```bash +cd "$SITE" +cat >> i18n/en.yaml <<'EOF' +docsMenu: "Documentation" +docsOnThisPage: "On this page" +docsPager: "Previous and next page" +EOF +cat >> i18n/it.yaml <<'EOF' +docsMenu: "Documentazione" +docsOnThisPage: "In questa pagina" +docsPager: "Pagina precedente e successiva" +EOF +``` + +- [ ] **Step 9: Rebuild CSS and run the check** + +```bash +cd "$SITE" && npm run build && hugo -D --gc --minify -d "$OUT" && bash "$OUT_CHECK" "$OUT" +``` +Expected: `PASS`. If the `rel=next` lines fail because prev and next are swapped, the installed Hugo differs from what Step 4 assumes: drop `.Reverse`. + +- [ ] **Step 10: Check by eye** + +Run `hugo server -D` and open `/hardenedpass/getting-started/concepts/` and `/it/hardenedpass/getting-started/concepts/` at 1280px and 390px width. Expected: sticky left column on desktop; two closed `
` on mobile; sticky column clears the site header. Fix `lg:top-24` if the header covers it. + +- [ ] **Step 11: Commit theme, then content** + +```bash +cd "$THEME" +git add layouts/hardenedpass layouts/partials/docs-nav.html layouts/partials/docs-tree.html layouts/partials/docs-pager.html layouts/index.json assets/css/main.min.css +git commit -m "feat: add layouts for the HardenedPass docs section" +cd "$SITE" +git add content/en/hardenedpass content/it/hardenedpass i18n themes/danix-xyz-hacker docs/superpowers/plans/2026-11-05-hardenedpass-docs.md +git commit -m "content: add HardenedPass docs skeleton as drafts" +``` +Each message ends with the `Co-Authored-By` line from the session. **Checkpoint: maintainer reviews the layout.** + +--- + +### Task 3: Emulator for screenshots + +- [ ] **Step 1: Start the environment** + +```bash +cd "$APP" +export ANDROID_SERIAL=emulator-5554 +scripts/e2e-env.sh up +"$ANDROID_HOME/emulator/emulator" -avd aps-test -no-audio -no-boot-anim -no-snapshot-save -gpu swiftshader_indirect & +./gradlew :app:installDebug +``` +Windowed: no `-no-window`. Needs the host's `libbsd`; if the emulator fails on it, stop and tell the maintainer. Follow the emulator notes in `$APP/AGENTS.md`: wait for boot and keyguard, disable animations, `settings put secure autofill_service null`, `scripts/e2e-env.sh push-keys`. The app is cloned from `ssh://git@10.0.2.2:2222/srv/git/store.git`. Unlike a real device, the file picker here shows only throwaway files, so the agent drives it. + +- [ ] **Step 2: Confirm secure screens capture** + +Open entry `example.org` and ask the maintainer to screenshot the emulator window. If the capture is black, secure screens stay text only and the shot lists below drop them. + +- [ ] **Step 3: Set language per pass** + +Capture everything in English first, then `adb shell "cmd locale set-app-locales xyz.danix.hardenedpass.debug --locales it"` and capture again. + +--- + +### Task 4: getting-started pages + +**Files:** `content/{en,it}/hardenedpass/getting-started/_index.md`, and `{concepts,install,create-store,connect}/index.md`. + +For each page: read the sources listed, verify each step, write EN and IT, fill `description` so that the first 160 characters name the topic, and use `{{< ref >}}` for internal links. Plain second person, short steps. Images are page bundle files or `/uppies/2026/11/`, through the `image` shortcode with `alt` text. End each page with no recap: the pager links onward. + +- [ ] **Step 1: `concepts`.** Password as an encrypted file, folder as a group, the first line is the password and later lines are metadata (`username:`, `url:`, `otpauth://`). OpenPGP public and private key and passphrase. Git as sync and history. How the three fit: `.gpg-id` names the recipients. Sources: `$APP/format/common/`, `https://www.passwordstore.org/`. Link out for depth. + +- [ ] **Step 2: `install`.** From GitHub Releases: `HardenedPass_vX.Y.Z.apk` and `.sha256`. `sha256sum -c` on desktop. Certificate SHA-256 from `$APP/AGENTS.md` under Releases, checked with `apksigner verify --print-certs`. Android's unknown sources prompt. Installs next to upstream, which is not migrated. Update by installing the newer APK; the same key is required. Check the exact release asset names against `gh release view v0.2.0 --repo danixland/HardenedPass`. + +- [ ] **Step 3: `create-store`.** On a desktop: `gpg --full-generate-key`, `pass init`, `pass git init`, private remote on any host, `pass git push`. Exporting the private key for the phone with `gpg --armor --export-secret-keys` and handling that file safely. Run every command in a temporary `GNUPGHOME` and `PASSWORD_STORE_DIR`, never the real ones. Check in `$APP` whether the app can generate a PGP key or create a store with no desktop, and document the local repository path that the welcome screen offers. + +- [ ] **Step 4: `connect`.** Welcome screen, clone, URL forms, SSH key import or generation, password auth, host key prompt, PGP import screen, then the store list. Source: the setup screens in `$APP/app/src/main/java/app/passwordstore/ui/`, and the URL warnings in `strings.xml` (`ssh_scheme_needed_title`, `https_scheme_with_port_title`, `git_scheme_disallowed_title`). + +- [ ] **Step 5: Screenshots.** Ask the maintainer for: welcome screen, repository info, SSH key screen, PGP import with recipients, store list. Both languages. + +- [ ] **Step 6: Build, check and commit** + +```bash +cd "$SITE" && hugo -D --gc --minify -d "$OUT" && bash "$OUT_CHECK" "$OUT" +git add content/en/hardenedpass content/it/hardenedpass static/uppies/2026/11 +git commit -m "content: write HardenedPass getting-started pages" +``` +Expected: `PASS`, no `REF_NOT_FOUND`. **Checkpoint: maintainer reviews, including the Italian.** + +--- + +### Task 5: using pages + +**Files:** `content/{en,it}/hardenedpass/using/_index.md`, and `{entries,otp,sync,autofill}/index.md`. Same method as Task 4. + +- [ ] **Step 1: `entries`.** Browsing, search and recursive filtering, opening an entry, copy and clipboard timeout, create, edit, move, delete, the random and diceware generators. Verify each on the emulator. + +- [ ] **Step 2: `otp`.** Storing an `otpauth://` line, the live code on the entry screen. The e2e store's `otp/example.com` shows it. The OTP screen never goes idle, so check it by eye. + +- [ ] **Step 3: `sync`.** Pull to sync, push, automatic commit on change, rebase on pull, conflict recovery (abort rebase and push a new branch, hard reset). Sources: `$APP/app/src/main/java/app/passwordstore/util/git/`, `BreakOutOfDetached`, `StoreActions`. + +- [ ] **Step 4: `autofill`.** Enabling the service, fill by chip or dropdown, the search chip, save flow, the phishing warning from `oreo_autofill_warning_publisher_dataset_title`, supported browsers from `$APP/autofill-parser/`. The emulator cannot do autofill (see `$APP/AGENTS.md`); write from the code and the 2026-11-05 tablet and Pixel runs. Firefox shows its own save prompt too: say so. + +- [ ] **Step 5: Screenshots.** Ask for: entry view with test data, editor, generator sheet, OTP code, conflict dialog if reachable. Autofill shots, if wanted, come from a real device with the test page and test store only: ask the maintainer. + +- [ ] **Step 6: Build, check and commit** + +```bash +cd "$SITE" && hugo -D --gc --minify -d "$OUT" && bash "$OUT_CHECK" "$OUT" +git add content/en/hardenedpass content/it/hardenedpass static/uppies/2026/11 +git commit -m "content: write HardenedPass using pages" +``` +**Checkpoint: maintainer review.** + +--- + +### Task 6: reference and landing pages + +**Files:** `content/{en,it}/hardenedpass/reference/_index.md`, `{settings,security,troubleshooting}/index.md`, `content/{en,it}/hardenedpass/_index.md`, `static/uppies/2026/11/hardenedpass-banner.png` (from `$APP/media/banner.png`). + +- [ ] **Step 1: `settings`.** Every setting, grouped as the Settings screen groups them. Start from `pref_*_title` in `$APP/app/src/main/res/values/strings.xml` and `values-it/strings.xml`, and confirm each is still shown in the Settings code: use the app's own Italian labels. + +- [ ] **Step 2: `security`.** Secrets are AES-GCM encrypted under Android Keystore keys, the PGP passphrase cache and its options, biometric unlock, `FLAG_SECURE`, no telemetry, no Play Services, the exported activity check on file paths. What it does not protect from: compromised unlocked device, a malicious keyboard, clipboard readers before the timeout, git host metadata (entry names are visible as file names). Sources: `$APP/AGENTS.md`, `KeystorePreferences.kt`. Every claim must be traceable to code. + +- [ ] **Step 3: `troubleshooting`.** Real errors from `strings.xml` with their cause and fix: wrong passphrase, no matching key, key without encryption subkey, host key changed, authentication failure, push rejected, detached HEAD. Source each one in the code path that raises it. + +- [ ] **Step 4: Landing page.** First person, site tone: why the fork exists, GrapheneOS goal, differences from upstream from `$APP/README.md`, credit to the Android Password Store authors, links to `install`, `connect`, GitHub and the issue tracker. Banner through the `image` shortcode. "GrapheneOS" appears as a goal only, never as part of a product name. + +- [ ] **Step 5: Build, check and commit** + +```bash +cd "$SITE" && hugo -D --gc --minify -d "$OUT" && bash "$OUT_CHECK" "$OUT" +git add content/en/hardenedpass content/it/hardenedpass static/uppies/2026/11 +git commit -m "content: write HardenedPass reference and landing pages" +``` +**Checkpoint: maintainer review.** + +--- + +### Task 7: Clean up and publish prep + +- [ ] **Step 1: Stop the environment** + +```bash +cd "$APP"; adb emu kill; scripts/e2e-env.sh down +``` + +- [ ] **Step 2: With the maintainer's approval only, flip drafts** + +```bash +cd "$SITE" +grep -rl '^draft = true' content/en/hardenedpass content/it/hardenedpass | xargs sed -i 's/^draft = true/draft = false/' +``` + +- [ ] **Step 3: Add the menu entry** + +In `hugo.toml`, for both `languages.it` and `languages.en`, change `legal` to `weight = 7` and add before it: + +```toml + [[languages.en.menus.main]] + identifier = "hardenedpass" + pageRef = "/hardenedpass" + name = "hardenedpass" + weight = 6 +``` +Use `languages.it.menus.main` in the Italian block. + +- [ ] **Step 4: Final build without `-D`** + +```bash +cd "$SITE" && hugo --gc --minify -d "$OUT" && bash "$OUT_CHECK" "$OUT" +grep -c hardenedpass "$OUT/en/index.json" "$OUT/it/index.json" 2>/dev/null || grep -rl 'hardenedpass' "$OUT" --include=index.json +``` +Expected: `PASS`, and the search index lists the pages. + +- [ ] **Step 5: Commit, then tell the maintainer** + +```bash +git add hugo.toml content/en/hardenedpass content/it/hardenedpass +git commit -m "content: publish the HardenedPass docs section" +``` +The maintainer pushes and merges `master` into `production`. + +- [ ] **Step 6: After the section is live, in `APP`** + +Add a `## Documentation` section to `README.md` linking `https://danix.xyz/hardenedpass/`, remove the Website item and the empty Documentation heading from `TODO.md`, and commit. -- cgit v1.2.3