diff --git a/packages/zapier-decisions/README.md b/packages/zapier-decisions/README.md index 58e5adb..febadd9 100644 --- a/packages/zapier-decisions/README.md +++ b/packages/zapier-decisions/README.md @@ -6,6 +6,7 @@ Lab log of architecture decisions, actions taken, and open todos while cleaning | File | What | |------|------| +| [STATUS.md](STATUS.md) | **Current status** (2026-09-12) | | [LOG.md](LOG.md) | Chronological actions | | [TODO.md](TODO.md) | Open items | | [decisions/](decisions/) | One file per decision | diff --git a/packages/zapier-decisions/STATUS.md b/packages/zapier-decisions/STATUS.md new file mode 100644 index 0000000..c0e6308 --- /dev/null +++ b/packages/zapier-decisions/STATUS.md @@ -0,0 +1,112 @@ +# Current status — 2026-09-12 + +Verae Time × Zapier is a **working lab** on one Proxmox host (**NS1.GEORGELAMBERT.ORG**, `70.88.205.138`). Public HTTPS doors, catalog, keep/fleet, and a **test 3-node NATS cluster** on separate LXC guests are up. The test environment **points at that cluster**. Zapier Platform `push` and live `api.veraetime.net` are still waiting on operator login / bind credentials. + +**Progress repo:** this file in [zapier-decisions](https://git.georgelambert.org/marchon/zapier-decisions). +**Catalog:** https://zapier.georgelambert.org/packages/zapier-decisions/STATUS.pdf +**Monorepo:** `master-zapier-plan-draft` (`main` / `master`). + +--- + +## Live right now + +| Layer | State | +|-------|--------| +| Catalog | https://zapier.georgelambert.org/ (PDF links path-relative; module/model books work) | +| Public doors | portal, api, zap, iam, mw, fleet, git — last off-box GET 8/8 OK | +| Keep + fleet | Running. systemd + linger **enabled** for next reboot (current processes were not double-started as systemd) | +| Test NATS | **3-node JetStream on `vmbr1`:** LXC 511/512/513 `nats-a/b/c` at `10.10.10.21–23`. Cluster name `verae`, 2 routes each. | +| Product streams | `ZAPIER_JOBS`, `ZAPIER_EVENTS`, `ZAPIER_WEBHOOKS`, `ZAPIER_USAGE`, `VERAE_ARCHIVE` — **replicas=3** | +| Modules on that cluster | Fleet, keep, middleware (`natsConnected: true`), billing, access planes. `NATS_URL` lists all three URLs. | +| Host loopback `:4222` | Still listening; **clients no longer use it** | +| Worker CT **510** `verae-px-worker` | `10.10.10.20` on `vmbr1`. Keep worm+tree **200** on the cluster | +| Backup | Timer `verae-backup.timer` enabled; snapshots under `/SSD2/backups/verae/` | +| Uptime | Loop on px-worker (`:3870/status`), last tick `failed: 0` | + +Client URL (private `vmbr1` only): + +```text +nats://10.10.10.21:4222,nats://10.10.10.22:4222,nats://10.10.10.23:4222 +``` + +Repo: https://git.georgelambert.org/marchon/verae-nats-cluster + +--- + +## Built and checked in (Forgejo, each its own repo) + +IAM, portal on access-web, keep/watch/guard, fleet floors, host-deps, catalog host-move docs, plus: + +| Repo | Job | +|------|-----| +| verae-uptime | Off-box HTTPS GET of public doors | +| verae-backup | SQLite / keep / worm-tree tar; restore dry-run default | +| verae-deploy | Tagged checkout + npm ci on the target OS | +| verae-proxmox | LXC 510 worker provision | +| verae-nats-cluster | LXC 511–513 JetStream cluster | + +--- + +## Still open + +| Item | Notes | +|------|--------| +| Zapier Platform `push` | Needs Zapier login (see below) | +| `MOCK_VERAE=false` | Live `api.veraetime.net` + admin bind credentials (see below) | +| NATS nkeys/mTLS | Cluster is up **without** accounts so existing `NATS_URL`s work. `verae-nats-accounts` remains the sketch. Flip only after every client has credentials. Isolation today is `vmbr1`, not nkeys. | +| lan-134 | Still disabled (SSH not working) | +| Reboot test | systemd should own keep/fleet after reboot; do not also `nohup guard.sh` | +| Hardware NATS | Same three configs, three boxes, private NIC — change IPs in `cluster.env` | + +--- + +## Once we have a Zapier login + +Zapier cloud only talks **HTTPS** to our Zap plane (`https://zap.zapier.georgelambert.org`). No NATS, no `api.veraetime.net`. + +1. `zapier login` (or `zapier-platform login`). +2. In `packages/verae-zapier` (and `verae-activate` if the tiny app is still wanted): + - `zapier-platform register` (or attach an existing app id). + - `MIDDLEWARE_BASE_URL=https://zap.zapier.georgelambert.org` (or `https://mw.zapier.georgelambert.org` if skipping the Zap plane). + - `zapier-platform build` and `validate`. +3. `zapier-platform push` as a **private** app. +4. Invite the workspace; create a Zap: + - Auth = portal **API key** (`zmw_…`), not a Verae JWT. + - Test **Create Timestamp and Wait**, **Create Timestamp** (async `jobId`), **Find Job Status**, **Timestamp Completed** REST Hook. +5. Fix anything `validate` / `push` flags (auth labels, samples, 402/403 mapping). +6. When the contract is stable, bump the platform version and invite a real user. + +Do **not** put NATS URLs or Verae passwords into Zapier. + +--- + +## Once `MOCK_VERAE=false` and live `api.veraetime.net` + +Middleware already has the live HTTP client. Still needed: + +1. `VERAE_API_BASE_URL=https://api.veraetime.net` and working **login** (username/password → JWT). JWT stays in middleware (`tokenRef`); Zapier never sees it. +2. Confirm OpenAPI vs our client: + - `POST /api/timestamp` → **202** `{ jobId }` + - `GET /api/status/{jobId}` until `completed` / `failed` + - `POST /api/verify` + - Batch create/status if used +3. **Hash lookup is not on the live OpenAPI** — `veraeClient.lookupHash` returns **501** when not mocking. Central-chain “find by SHA-256” needs a Verae API gap fill or our own store. +4. Map live status JSON into the receipt shape the Zap app expects (`certificate`, `timestamp`, `blockIndex`, `sha256`). +5. Token refresh, 401 retry, and 402/403 mapping against real errors. +6. One live seal in the lab, then REST Hook delivery to `hooks.zapier.com`. + +--- + +## Submit → jobId → wait → receipt + +**Yes — that is the designed path**, and it already runs in **mock**. + +1. Zap (or curl) `POST /zapier/v1/timestamp` or `/timestamp/wait`. +2. Middleware `createTimestamp` calls Verae `POST /api/timestamp` (or the mock) and gets **`jobId`**. +3. It publishes **`verae.zapier.jobs.watch`** on NATS (the 3-node cluster). +4. **Wait:** subscribe to **`verae.zapier.jobs.events`** for that `jobId`, or poll `GET /api/status/{jobId}`. +5. Job-poller hits Verae status until `completed`. +6. Event `timestamp.completed` carries status; webhook-deliver POSTs the Zapier REST Hook. +7. Response includes **certificate / timestamp / blockIndex** (mock uses `mock-cert-{jobId}`, `blockIndex: 42`). Async create returns `{ jobId, sha256, existing }` immediately; wait returns the finished status. Same SHA-256 is idempotent (`existing: true`). + +If wait times out, middleware returns **`pending` + `jobId`** so the hook can finish the Zap. Live Verae must match that HTTP contract; we have **not** proven a real chain seal yet (`MOCK_VERAE` is still true). diff --git a/packages/zapier-decisions/TODO.md b/packages/zapier-decisions/TODO.md index 57b81c5..d929275 100644 --- a/packages/zapier-decisions/TODO.md +++ b/packages/zapier-decisions/TODO.md @@ -1,7 +1,8 @@ # Open todos - [ ] Live `api.veraetime.net` with `MOCK_VERAE=false` and admin bind credentials. -- [ ] NATS nkeys/mTLS on a real three-node cluster (accounts file is the lab stand-in). +- [x] Three-node NATS JetStream cluster on distinct Proxmox LXC (`verae-nats-cluster`; test env `NATS_URL` cut over; streams replicas=3). +- [ ] NATS nkeys/mTLS on that cluster (accounts file is still the lab stand-in; not flipped so clients keep working on `vmbr1`). - [x] Exclusive JetStream consumer for `verae.zapier.jobs.events` on `verae-jobs-events` (`JOBS_EVENTS_EXCLUSIVE=1`; middleware skips the router). - [x] Auth on CS/sales/accounting HTML via `verae-staff-session` (`STAFF_AUTH=1`). - [x] Staff IAM: named users, roles, permissions (`verae-staff-iam` :3028). diff --git a/scripts/build-docs-site.py b/scripts/build-docs-site.py index 3d4b134..03b6925 100755 --- a/scripts/build-docs-site.py +++ b/scripts/build-docs-site.py @@ -625,7 +625,7 @@ def main() -> None: else: extras = ("README.md", "SUMMARY.md", "NATS.md") if pkg == "zapier-decisions": - extras = extras + ("LOG.md", "TODO.md") + extras = extras + ("LOG.md", "TODO.md", "STATUS.md") if pkg == "verae-bootstrap": extras = extras + ("HOST-DEPS.md",) for name in extras: @@ -850,6 +850,7 @@ def main() -> None: ("packages/verae-zapier-simulator/README.pdf", "Simulator README"), ("packages/verae-fleet/docs/CONSOLE.pdf", "Operator console"), ("packages/ui-docs/UI-REVIEW.pdf", "UI review (screenshots + live doors)"), + ("packages/zapier-decisions/STATUS.pdf", "Current lab status (2026-09-12)"), ("overview/README.pdf", "System overview"), ("overview/INDEX.pdf", "Documentation index"), ("docs/modules-pdf/index.pdf", "Module PDFs (book)"), @@ -861,6 +862,7 @@ def main() -> None: ("docs-master/README.html", "Master summaries (docs-master)"), ("docs-master/CATALOG-HOST.html", "Move the catalog to another hostname"), ("docs-master/HOST-DEPS.html", "Host OS / Node / NATS installer"), + ("packages/zapier-decisions/STATUS.html", "Current lab status"), ("docs-master/MESSAGE-FLOWS.html", "Numbered message flows"), ("docs-master/modules-and-nats.html", "NATS address table"), ("packages/verae-bootstrap/HOST-DEPS.html", "Host OS / Node / NATS installer"),