docs: audit corrections, coverage pages, screenshots, and em-dash removal

Finalizes the docs section after an adversarial audit (code review + per-page
fact-check + coherence critic) and two content requests.

- Accuracy: fixed 10 invented cross-link slugs, the admin-vs-board gating claims
  the verifiers caught (permissions matrix, /settings, invites, onboarding,
  connect-payments, proposal withdraw, Stripe reconciliation, member fields,
  door.operate), and 2 issues in the new pages (project area-at-creation, forum
  thread-delete role).
- Coverage: added 5 how-to pages so every module is documented (recurring Stripe
  dues, tasks & chores, projects, comms & forum, ops knowledge base + secrets
  vault). 34 pages total.
- Screenshots: 12 captioned app screenshots under public/docs-media/, embedded
  as figures. Each tutorial now has 2-3 step screenshots.
- Style: removed all em-dashes/en-dashes/ellipses from the docs (natural ASCII
  punctuation).
- Handoff (docs/HANDOFF-audit-cleanup.md) updated with the current state.

typecheck 0 · lint 0 errors · build ok (all 34 /docs pages prerendered)
This commit is contained in:
Moheeb Zara
2026-08-05 03:15:07 -07:00
parent 16229ae523
commit 8edc9da611
50 changed files with 610 additions and 202 deletions
+6 -6
View File
@@ -4,15 +4,15 @@ You need the `door.manage` permission (held by `admin` and `board` by default) t
## Before you start
Store the controller's shared password in your encrypted Secrets vault under [/ops](/ops) first. The password is never stored on the connection itself — the connection only references a vault entry by title, and the server decrypts it at call time. If your controller has no password, you can skip this.
Store the controller's shared password in your encrypted Secrets vault under [/ops](/ops) first. The password is never stored on the connection itself, the connection only references a vault entry by title, and the server decrypts it at call time. If your controller has no password, you can skip this.
## Add the connection
1. Open [/door/manage](/door/manage) (the "Door access" screen) and click **New connection**.
2. Enter a **Name** (for example, `Front door`).
3. Pick the **adapter**:
- **HeatSync / 23b Open Source Access Control (native)** — the verified Arduino query-string firmware. Verb encoding is built in; you configure nothing else.
- **Other / generic HTTP controller** — any controller you can drive over HTTP, using per-verb templates (see below).
- **HeatSync / 23b Open Source Access Control (native)**, the verified Arduino query-string firmware. Verb encoding is built in; you configure nothing else.
- **Other / generic HTTP controller**, any controller you can drive over HTTP, using per-verb templates (see below).
4. Choose the vault password from the second dropdown, or leave **No password (auth: none)**.
5. Set the **Base URL** (for example, `http://192.168.1.50/`) and the **Pinned host** (for example, `192.168.1.50`).
6. Click **Create connection**.
@@ -35,11 +35,11 @@ Leave any verb blank if the controller does not support it. Example: `?m{slot}&p
## Verify it
Click **Test** on the connection. This runs the read-only `status` verb only — it never opens the door — and writes one redacted row to the access log. A success toast shows the HTTP status.
Click **Test** on the connection. This runs the read-only `status` verb only (it never opens the door) and writes one redacted row to the access log. A success toast shows the HTTP status.
## Assign member cards
With `door.operate`, click **Cards** in the Operate row. You get every active card in the space. Click **Grant** to write a card to the controller; the platform allocates the lowest free slot (HeatSync uses slots `0`–`200`) and shows it. Click **Revoke** to remove it. Slots are per connection, so the same card can hold different slots on different controllers, and re-granting is idempotent.
With `door.operate`, click **Cards** in the Operate row. You get every active card in the space. Click **Grant** to write a card to the controller; the platform allocates the lowest free slot (HeatSync uses slots `0`-`200`) and shows it. Click **Revoke** to remove it. Slots are per connection, so the same card can hold different slots on different controllers, and re-granting is idempotent.
The Operate row also gives you **Open** (momentary), **Unlock** (hold open), and **Lock**.
@@ -49,7 +49,7 @@ To pull actual entry and denied events into the log, click **Inbound**:
1. Choose a **webhook secret** from your vault (distinct from the door password) and click **Turn inbound on**.
2. For a generic controller, POST events to the shown webhook URL, `/api/door/inbound/<connection-id>`, with header `Authorization: Bearer <your webhook secret>` and a JSON body like `{ "events": [{ "id": "<stable-id>", "card_uid": "<hex>", "result": "granted" }] }`. The `id` dedupes retries.
3. For a HeatSync connection, a once-a-minute poll reads the controller's log automatically once inbound is on — no webhook wiring needed.
3. For a HeatSync connection, a once-a-minute poll reads the controller's log automatically once inbound is on, no webhook wiring needed.
Ingested events resolve each presented card to a member and appear in the **Access log** at the bottom of the page. That log is append-only and immutable: only the server writes it, secrets are redacted first, and rows survive even if you later delete the connection.