|
| 1 | +# Reproduction checklist: someone else builds Coldframe (Story 10.5) |
| 2 | + |
| 3 | +This checklist is the test of Coldframe's promise that it can be reproduced from the public |
| 4 | +repository alone (success criterion SM-5). It walks the [build guide](../build/README.md) and the |
| 5 | +[deploy guide](../deploy/README.md) end to end, from the parts to a Threshold Alert on your phone. |
| 6 | +It does not repeat their commands: **the guides are what is under test**. Each part names the guide |
| 7 | +section that does the work and lists what you must observe when it is done. |
| 8 | + |
| 9 | +Who may run it: |
| 10 | + |
| 11 | +- **A person other than the author**, using only this public repository and its docs; or |
| 12 | +- **the author on a clean machine with fresh hardware and no prior state**: no cached toolchain, no |
| 13 | + existing cluster, no Keycloak realm, no enrolled boards, no app build or account left over from |
| 14 | + earlier work. Part A records that attestation; a run without it does not count. |
| 15 | + |
| 16 | +Nobody helps you beyond what is written in the repository. When a guide is unclear, wrong or |
| 17 | +incomplete, that is a **stuck point**: do not ask the author, file it |
| 18 | +([When you get stuck](#when-you-get-stuck)) and work around it only if the guide's own text lets |
| 19 | +you. Every stuck point is fixed in the docs before the story closes. |
| 20 | + |
| 21 | +At the end you write a result record from [`results/TEMPLATE.md`](results/TEMPLATE.md) and commit |
| 22 | +it under [`results/`](results/README.md). |
| 23 | + |
| 24 | +## What you need |
| 25 | + |
| 26 | +- **A release tag** `vX.Y.Z` of this repository with its GitHub Release (firmware binaries and |
| 27 | + `SHA256SUMS`): [`docs/operations/release.md`](../operations/release.md). Use the same tag for |
| 28 | + everything below. |
| 29 | +- **The parts for one Node and one Hub** from the [bill of materials](../../hardware/bom.md), plus |
| 30 | + USB cables, the soil the probe goes into, and the tools to assemble the Node. |
| 31 | +- **A home server** that runs all the time, as listed in the deploy guide's |
| 32 | + [Before you start](../deploy/README.md#before-you-start). |
| 33 | +- **A domain** whose DNS zone is hosted at Cloudflare, and a local DNS resolver you control. |
| 34 | +- **An SMTP account** that can send mail from an address of yours. |
| 35 | +- **An S3 bucket** off the home server for the database backups. |
| 36 | +- **One or two phones**: an iPhone with a Mac, Xcode and an Apple Developer Program membership, |
| 37 | + and/or an Android phone with a Firebase project |
| 38 | + ([what the apps need](../build/apps.md#what-you-need)). |
| 39 | +- **A 2.4 GHz Wi-Fi network** with WPA2-Personal or mixed WPA2/WPA3. WPA3-only is not supported |
| 40 | + ([`flash.md`, Wi-Fi](../build/flash.md#wi-fi)). |
| 41 | +- **A clean machine** to run the commands and builds from, with nothing of Coldframe on it yet. |
| 42 | +- A notebook (paper or a local file outside the repository) for the time each part takes and every |
| 43 | + stuck point. |
| 44 | + |
| 45 | +Keep every key, token, password and setup code out of the repository, out of issues and out of the |
| 46 | +result record. |
| 47 | + |
| 48 | +## A. Start clean |
| 49 | + |
| 50 | +- [ ] You are not the author, **or** you are the author and every item below is true. Write which in |
| 51 | + the result record. |
| 52 | + |
| 53 | +The remaining items are required for an author run. A non-author ticks each one only where it is |
| 54 | +true and writes n/a in the result record otherwise. |
| 55 | + |
| 56 | +- [ ] The machine you run the commands and builds from has no esp toolchain, `espflash`, Rust |
| 57 | + target cache, Gradle or Xcode build cache for Coldframe, and no kubeconfig for a Coldframe |
| 58 | + cluster. |
| 59 | +- [ ] The home server has no RKE2, Fleet or Coldframe install on it. |
| 60 | +- [ ] The Hub and Node boards are fresh: never flashed with Coldframe (Part D checks the key |
| 61 | + blocks). |
| 62 | +- [ ] No Keycloak realm, account, Site or Device of an earlier Coldframe install exists anywhere you |
| 63 | + will use, and no Coldframe app is installed on the phones. |
| 64 | +- [ ] No Firebase project, APNs key, Cloudflare API token or S3 bucket made for an earlier Coldframe |
| 65 | + install is reused. |
| 66 | +- [ ] You have the repository from its public URL, checked out at the release tag of Part B, and no |
| 67 | + other notes than the docs in it. |
| 68 | + |
| 69 | +## B. The release |
| 70 | + |
| 71 | +Follow [`docs/operations/release.md`, What gets published](../operations/release.md#what-gets-published) |
| 72 | +and [Verify a download](../operations/release.md#verify-a-download). |
| 73 | + |
| 74 | +- [ ] The GitHub Release for `vX.Y.Z` exists and carries `coldframe-hub-X.Y.Z.bin`, |
| 75 | + `coldframe-node-X.Y.Z.bin` and `SHA256SUMS`. |
| 76 | +- [ ] The download checks: with every Release asset downloaded next to `SHA256SUMS`, every line of |
| 77 | + the checksum check says `OK`. |
| 78 | +- [ ] The release notes link the build-your-own-apps guide. |
| 79 | +- [ ] Your clone is at that tag; the deploy guide, the firmware and the apps are all built from it. |
| 80 | + |
| 81 | +## C. Build the Node and the Hub |
| 82 | + |
| 83 | +Follow the [build guide](../build/README.md), steps |
| 84 | +[1. Get the parts](../build/README.md#1-get-the-parts) and |
| 85 | +[2. Assemble](../build/README.md#2-assemble). |
| 86 | + |
| 87 | +- [ ] Every part you bought matches a line of the [BOM](../../hardware/bom.md), or a substitute meets |
| 88 | + that line's requirement. |
| 89 | +- [ ] The Node is wired exactly as in [`hardware/node.md`](../../hardware/node.md), without guessing a |
| 90 | + connection the page does not state. |
| 91 | +- [ ] The Node sits in its enclosure with the soil probe sealed as in |
| 92 | + [`hardware/enclosure.md`](../../hardware/enclosure.md). |
| 93 | +- [ ] The Hub is a dev board on USB power, placed as in [`hardware/hub.md`](../../hardware/hub.md). |
| 94 | +- [ ] The panel placement follows [power.md](../build/power.md#panel-placement). |
| 95 | + |
| 96 | +## D. Flash the Hub and the Node |
| 97 | + |
| 98 | +Follow [3. Install the toolchain](../build/README.md#3-install-the-toolchain), |
| 99 | +[4. Flash the Hub](../build/README.md#4-flash-the-hub) and |
| 100 | +[5. Flash the Node](../build/README.md#5-flash-the-node), through |
| 101 | +[`flash.md`](../build/flash.md). **Read its eFuse warning first**: the release binaries burn an |
| 102 | +eFuse key on the first boot, and that cannot be undone. Only boards meant to stay a Hub or a Node |
| 103 | +get them. |
| 104 | + |
| 105 | +- [ ] The toolchain reports the versions [`flash.md`, Toolchain](../build/flash.md#toolchain) pins |
| 106 | + (at least `espflash` for the release binaries). |
| 107 | +- [ ] [The key-purpose check](../build/flash.md#before-you-flash-check-the-key-purposes) shows at |
| 108 | + least one unused `USER` key block on each board before you flash it. |
| 109 | +- [ ] The Hub's [first boot](../build/flash.md#first-boot-and-the-setup-code) logs its version |
| 110 | + `X.Y.Z`, `source=burned KEYn` once, a setup code, and that it advertises its BLE setup |
| 111 | + service. A reboot logs `source=existing KEYn` with the same Device ID. |
| 112 | +- [ ] The Node's first wake logs its version and its identity, then measures and sleeps. Its first |
| 113 | + wake logs `source=burned KEYn` once; a later wake logs `source=existing KEYn` with the same |
| 114 | + Device ID. |
| 115 | +- [ ] A long press (3 s) of the Node's setup button |
| 116 | + ([the buttons](../build/flash.md#the-buttons)) logs `wake plan=setup`, a setup code and a |
| 117 | + 180 s setup window (`window_ms=180000`). |
| 118 | +- [ ] Each setup code is on a sticker on its Device, and nowhere else. |
| 119 | + |
| 120 | +## E. Deploy the Server at home |
| 121 | + |
| 122 | +Follow the [deploy guide](../deploy/README.md), sections |
| 123 | +[1. Domain and DNS API token](../deploy/README.md#1-domain-and-dns-api-token) to |
| 124 | +[6. First sign-in](../deploy/README.md#6-first-sign-in), in order: single-node RKE2 with Fleet, |
| 125 | +Let's Encrypt certificates through DNS-01, split DNS. The Compose example does not count. |
| 126 | + |
| 127 | +- [ ] RKE2 is installed and `kubectl` shows one node, Ready. |
| 128 | +- [ ] Fleet is installed, and every required Secret of section 3 exists before the GitRepo is |
| 129 | + applied (the optional `coldframe-push` may follow in Part F). |
| 130 | +- [ ] The `coldframe-realm` ConfigMap is written with `prepare-realm.sh` for `https://<domain>` |
| 131 | + ([section 4](../deploy/README.md#4-keycloak-realm-and-phase-two)), and Keycloak imports the |
| 132 | + `coldframe` realm on its first start. |
| 133 | +- [ ] The GitRepo points at your release tag, and every bundle becomes Ready. |
| 134 | +- [ ] From a laptop on the home network the three hosts (`<domain>`, `api.<domain>`, |
| 135 | + `auth.<domain>`) resolve to the home server's LAN address and serve a Let's Encrypt |
| 136 | + certificate with no warning. |
| 137 | +- [ ] You register in the web app, the verification email arrives through your SMTP account, and |
| 138 | + you create a Site. |
| 139 | +- [ ] Keycloak's temporary bootstrap admin is replaced by a permanent admin in the master realm and |
| 140 | + deleted ([section 6](../deploy/README.md#6-first-sign-in)), and the `coldframe-keycloak-admin` |
| 141 | + Secret is kept. |
| 142 | +- [ ] Every item of the |
| 143 | + [verification checklist on the home server](../operations/install.md#verification-checklist-on-the-home-server) |
| 144 | + is ticked, including a backup in your S3 bucket. |
| 145 | + |
| 146 | +## F. Build your own apps |
| 147 | + |
| 148 | +Follow [`docs/build/apps.md`](../build/apps.md) from the same release tag: |
| 149 | +[Android](../build/apps.md#android) and/or [iOS](../build/apps.md#ios), with your own ids, then |
| 150 | +[Server push](../build/apps.md#server-push). |
| 151 | + |
| 152 | +- [ ] Each app builds with your Server URL and Keycloak issuer, and installs on your phone. No key, |
| 153 | + team ID, keystore or `google-services.json` was added to the repository (`git status` is |
| 154 | + clean apart from the git-ignored files the guide names). |
| 155 | +- [ ] Each app signs in on the home Wi-Fi and shows the Site of Part E, with no Unreachable notice. |
| 156 | +- [ ] The Server's log has `PushChannelConfigured` for every provider you set up. |
| 157 | +- [ ] Each app asks for notification permission, and you allow it. |
| 158 | + |
| 159 | +## G. Add the Hub and the Node, calibrate |
| 160 | + |
| 161 | +Follow [6. Add them from the app](../build/README.md#6-add-them-from-the-app). |
| 162 | + |
| 163 | +- [ ] **Add a Hub**: the app finds the unprovisioned Hub over BLE, takes its setup code from the |
| 164 | + sticker, and sends it your 2.4 GHz WPA2 (or mixed WPA2/WPA3) network and your Server's |
| 165 | + address. The app reports the Hub added to the Site, and its serial output no longer prints a |
| 166 | + setup code on boot. |
| 167 | +- [ ] Create a Lot for the Node if the Site has none. |
| 168 | +- [ ] **Add a Node**: long-press its setup button (3 s), pick the Lot, and enter its setup code |
| 169 | + within the 180 s setup window. The Node is listed on that Lot. |
| 170 | +- [ ] A short press of the Node's button sends a Reading now, and the Lot shows it. |
| 171 | +- [ ] Put the probe in the soil it stays in and leave it **2–3 days to settle** |
| 172 | + ([why](../build/power.md#soil-moisture)). Readings keep arriving the whole time. |
| 173 | +- [ ] Calibrate the soil Sensor in the app (a dry point and a wet point). Afterwards the Lot shows |
| 174 | + soil moisture as a % (a multiple of 5) and the Sensor no longer asks for Calibration. |
| 175 | +- [ ] The Lot's low Threshold is the default **30 %** unless you changed it; note the value you use. |
| 176 | + |
| 177 | +## H. A Threshold Alert inside the Notification Window |
| 178 | + |
| 179 | +The Alert flow is described in the |
| 180 | +[push checklist, Part D](../bench/push-checklist.md#d-an-alert-on-both-phones). Do this inside your |
| 181 | +Notification Window (My notifications; the default is **07:00 to 22:00** in your time zone), with |
| 182 | +the app in the background. |
| 183 | + |
| 184 | +- [ ] Let the Lot's soil read below its low Threshold for **three consecutive Readings**: take the |
| 185 | + probe out of the soil (a short press sends each Reading now). |
| 186 | +- [ ] Within a minute of the third Reading each phone shows **one** notification titled |
| 187 | + "‹Lot› needs water", with the body "~‹value› % in the soil, your low is ‹low› %." |
| 188 | +- [ ] Tapping the notification opens Lot detail of that Lot. |
| 189 | +- [ ] Put the probe back and let the Lot recover (three Readings within the Thresholds): the Lot |
| 190 | + shows within its Thresholds again (the Alert closes), and no new notification arrives. |
| 191 | + |
| 192 | +## When you get stuck |
| 193 | + |
| 194 | +A stuck point is any place where the docs alone did not get you to the next check: a missing step, |
| 195 | +a wrong command or value, a link that leads nowhere useful, an unexplained error, or a guess you had |
| 196 | +to make. |
| 197 | + |
| 198 | +1. Open a new issue on GitHub and choose the **Reproduction stuck point** form. Name the part |
| 199 | + (A–H), the guide page and section, what you did, what you expected and what happened. Paste no |
| 200 | + secret, setup code, token or private domain credentials. |
| 201 | +2. Write the issue number in your notes, next to the part. |
| 202 | +3. Carry on only if you can without the author's help; otherwise stop the run there and record the |
| 203 | + result as failed at that part. |
| 204 | + |
| 205 | +Each stuck point is fixed by a pull request to the docs. The story stays open until every issue |
| 206 | +listed in the result record is fixed. After a doc fix merges, rerun the affected parts following |
| 207 | +the fixed docs on `main`, with the same release artifacts and the boards already flashed, and note |
| 208 | +the rerun in the same record. A fix in the firmware, the Server, the charts or the apps ships in a |
| 209 | +new release tag; the run then continues from the first affected part on that tag. |
| 210 | + |
| 211 | +## Record the result |
| 212 | + |
| 213 | +Copy [`results/TEMPLATE.md`](results/TEMPLATE.md) to `docs/reproduce/results/YYYY-MM-DD-<tester>.md`, |
| 214 | +fill it in, and open a pull request with it ([`results/README.md`](results/README.md) has the |
| 215 | +rules). Fill it in truthfully, including the parts that failed. |
| 216 | + |
| 217 | +## Result |
| 218 | + |
| 219 | +Pass: parts A to H all ticked, with the attestation of Part A, on one release tag, and every |
| 220 | +stuck-point issue raised during the run fixed in the docs. A run where A to H passed but listed |
| 221 | +issues are still open is **pending doc fixes**; it becomes a pass once every listed issue's fix is |
| 222 | +merged. Anything else is a failed run of Story 10.5, and SM-5 does not hold until a later run |
| 223 | +passes. |
0 commit comments