125 lines
5.8 KiB
Markdown
125 lines
5.8 KiB
Markdown
# Zapier → Verae API → Zapier
|
|
|
|
How a timestamp job is submitted, watched, and returned. Lab today: `MOCK_VERAE=true`. Live path is the same HTTP contract with `MOCK_VERAE=false`.
|
|
|
|
## Rule
|
|
|
|
```text
|
|
Zapier cloud --HTTPS only--> zap.zapier.georgelambert.org (access-zapier)
|
|
|
|
|
v HTTPS
|
|
middleware :3100 /zapier/v1
|
|
|
|
|
+---------------+----------------+
|
|
| HTTPS | NATS (private cluster)
|
|
v v
|
|
api.veraetime.net jobs.watch → poller
|
|
POST /api/timestamp GET /api/status/{jobId}
|
|
GET /api/status/{jobId} |
|
|
POST /api/verify v
|
|
jobs.events
|
|
|
|
|
+--------------------+--------------------+
|
|
| wait subscriber | webhook-deliver
|
|
v v
|
|
HTTP response to Zap POST hooks.zapier.com
|
|
```
|
|
|
|
Zapier never has `NATS_URL`. Verae never has `NATS_URL`.
|
|
|
|
## 1. Submit (async create)
|
|
|
|
1. Zap action **Create Timestamp** (or curl) `POST https://zap.zapier.georgelambert.org/zapier/v1/timestamp` with portal API key `zmw_…` (not a Verae JWT).
|
|
2. access-zapier / edge: authz, then `meter()` records cents; if `NATS_URL` is set, `natsPublish(verae.billing.usage.recorded)`.
|
|
3. Forwards to middleware `POST /zapier/v1/timestamp`.
|
|
4. Middleware `checkEntitlement` (local plan limits today).
|
|
5. `veraeClient.createTimestamp`:
|
|
- **Mock:** in-process job, fake `jobId`.
|
|
- **Live:** `POST https://api.veraetime.net/api/timestamp` with JWT → **202** `{ jobId }`.
|
|
6. Splitter (in-process): hash vs attachments; optional `verae.archive.put` (bytes never go to chain).
|
|
7. `enqueueWatch` → JetStream **`verae.zapier.jobs.watch`**.
|
|
8. HTTP **202** `{ jobId, sha256, existing }` back to Zapier immediately.
|
|
|
|
Same SHA-256 is idempotent (`existing: true`).
|
|
|
|
## 2. Monitor (job poller)
|
|
|
|
The poller is **in the middleware Node process** when `NATS_ENABLED=true` (`startJobPollerWorker`). Fleet does **not** spawn a second copy (`job-poller.enabled: false`).
|
|
|
|
1. Durable consumer `job-poller` on `ZAPIER_JOBS` / `verae.zapier.jobs.watch`.
|
|
2. Resolve `tokenRef` → tenant Verae password → `POST` login → JWT (stays in middleware).
|
|
3. `GET /api/status/{jobId}` (mock or live timestamp server).
|
|
4. `pending` → `nak` after `intervalMs` (retry).
|
|
5. `completed` → publish `jobs.events` `{ event: timestamp.completed, status, jobId, tenantId, traceId }`.
|
|
6. `failed` → `timestamp.failed`. Too many attempts → `timestamp.timeout`.
|
|
7. `ack` the watch message.
|
|
|
|
Live OpenAPI: poll until `completed` / `failed`. Hash lookup is **501** on live (`lookupHash`). Map `certificate`, `timestamp`, `blockIndex`, `sha256` into the receipt Zap samples expect.
|
|
|
|
## 3. Wait (sync Zap step)
|
|
|
|
**Create Timestamp and Wait** → `POST /zapier/v1/timestamp/wait`.
|
|
|
|
1. Same as submit (watch is already on NATS).
|
|
2. `waitForJobEvent(jobId, WAIT_TIMEOUT_MS)` subscribes to **`verae.zapier.jobs.events`**.
|
|
3. On matching `jobId` event: return status JSON (certificate / receipt).
|
|
4. On timeout: `{ status: pending, jobId }` so the REST Hook can finish the Zap.
|
|
|
|
`includeAttached`: after seal, aggregator should `verae.archive.query`; bloom hits merge extra receipts. **Simulator does this; live aggregator hook is TBD.**
|
|
|
|
## 4. Push back to Zapier
|
|
|
|
- **Wait response:** HTTP body on the same Zap step.
|
|
- **REST Hook:** Zapier subscribes `POST /zapier/v1/hooks`. On `jobs.events`, middleware enqueues **`verae.zapier.webhooks.deliver`**. Worker POSTs `targetUrl` (`hooks.zapier.com`).
|
|
- **Poll status:** Zap action **Find Job Status** → `GET /zapier/v1/status/{jobId}` → middleware `GET` Verae (or last event).
|
|
|
|
## 5. Verify
|
|
|
|
`POST /zapier/v1/verify` → middleware → Verae `POST /api/verify`. No job watch.
|
|
|
|
## 6. Batch Merkle (designed; same bus)
|
|
|
|
From `MESSAGE-FLOWS.md`:
|
|
|
|
1. `POST /v1/timestamp/batch` — one item per line.
|
|
2. Middleware hashes leaves, builds Merkle tree.
|
|
3. Chain seals **only the root** (`POST /api/timestamp` on the root).
|
|
4. Each leaf proof → `verae.archive.put` `kind: tree`.
|
|
5. Central hash lookup of a leaf misses unless `includeTree=true`.
|
|
6. Aggregator broadcasts `verae.archive.query` `kinds=["tree"]`.
|
|
7. Tree node bloom hit → `archive.reply.<id>` with proof.
|
|
|
|
Live wait path does not yet call the aggregator, so tree-leaf receipts are not merged on NS1.
|
|
|
|
## Connect live `api.veraetime.net`
|
|
|
|
Still blocked on operator bind credentials. When ready:
|
|
|
|
```bash
|
|
# middleware env
|
|
MOCK_VERAE=false
|
|
VERAE_API_BASE_URL=https://api.veraetime.net
|
|
NATS_ENABLED=true
|
|
NATS_URL=nats://10.10.10.21:4222,nats://10.10.10.22:4222,nats://10.10.10.23:4222
|
|
JETSTREAM_REPLICAS=3
|
|
```
|
|
|
|
Then: working Verae login, one live seal, confirm 202/status/verify, map receipt fields, REST Hook to a private Zap. See `zapier-decisions/STATUS.md` “Once MOCK_VERAE=false”.
|
|
|
|
## Billing on this path
|
|
|
|
| Hop | Designed | Running |
|
|
|-----|----------|---------|
|
|
| Edge meter | `verae.billing.usage.recorded` | **coded**; fires when edge has `NATS_URL` |
|
|
| Account-balance | subscribe + debit prepaid; 402 if empty | **subscriber running**; 402 from books not proven on every Zap |
|
|
| Middleware quota | local files via `recordUsage` | **running**; not NATS |
|
|
| `verae.zapier.usage` | optional JS meter | stream only |
|
|
|
|
## Connect a new internal process
|
|
|
|
```bash
|
|
export NATS_URL=nats://10.10.10.21:4222,nats://10.10.10.22:4222,nats://10.10.10.23:4222
|
|
# clone verae-nats-process, rename subjects, register in verae-fleet
|
|
```
|
|
|
|
Jobs stay: HTTP in from Zapier → HTTPS to Verae → NATS watch/events → HTTP back to Zapier.
|