Milestone 0: import zappier billing, Verae middleware, and Zapier research

Compose-ready workspace: packages/zappier (rate card, portal, Stripe),
packages/verae-zapier-middleware (timestamp + NATS), packages/verae-zapier
(CLI app), vendor/zapier-platform, and research/zapier vendor corpus.

Gate 0 structure checks pass. Product code and research are not yet wired.
This commit is contained in:
George Lambert 2026-09-09 02:37:36 -04:00
commit b4150c8250
1364 changed files with 6814366 additions and 0 deletions

View file

@ -0,0 +1,729 @@
# Getting started — Verae Time × Zapier
**System architecture, functionality, integration guide, and next steps**
This is the working document for the research workspace at `/Users/marchon/research/zapier`. It covers the full stack that connects Zapier automations to the Verae Timestamping Service, how to run and extend it locally, Zapier vs Verae billing, a client pattern for timestamped files on Peergos/IPFS with cold retrieve-on-demand, and what remains before a private Zapier listing.
Grok (and humans) should start here, then follow the playbook in [PLATFORM-REFERENCE.md](PLATFORM-REFERENCE.md).
---
## 1. How to start this workspace
```bash
cd /Users/marchon/research/zapier
./scripts/restart-grok.sh # Mongo tunnel + grok --resume
```
Already in Grok:
```
/zapier-build
```
Mandatory first query (same playbook as [PLATFORM-REFERENCE.md](PLATFORM-REFERENCE.md)):
```js
db.platform_reference.findOne({ kind: "guide", key: "build-new-connector" })
```
Keep the Mongo tunnel up (`./scripts/ensure-mongo-tunnel.sh`). Check readiness with `./scripts/zapier-status.sh`. Load CLI PATH and env with `source scripts/dev-env.sh`.
Leave the TUI with `/quit` (not `/new`). Restart details: [RESTART.md](RESTART.md).
---
## 2. System architecture
### 2.1 Purpose
Connect Zapier automations to Verae blockchain timestamping **without**:
- exposing raw Verae JWTs to Zapier end users
- requiring Zapier to poll async jobs on `api.veraetime.net`
- coupling billing and plan limits to the core timestamping API
- running a multi-instance edge with only in-memory job queues
### 2.2 Bottom line
![High-level architecture: Zapier cloud, middleware, NATS workers, Verae API, and REST Hooks](docs/diagrams/01-high-level-architecture.svg)
```text
Users → Zapier UI
Zapier cloud runs the Platform CLI app (verae-zapier or scratch/veraetime)
→ HTTPS only → verae-zapier-middleware /zapier/v1/*
→ (sync) HTTPS → https://api.veraetime.net
→ (async) NATS JetStream → workers
→ HTTPS → api.veraetime.net (status poll)
→ HTTPS → hooks.zapier.com (REST Hook delivery)
```
- Zapier **never** connects to NATS.
- Zapier **never** calls `api.veraetime.net` directly.
- Middleware HTTP owns auth, tenancy, entitlements, metering, and the public API surface.
- NATS owns durable job watching, completion events, and reliable webhook delivery (when `NATS_ENABLED=true`).
- With `NATS_ENABLED=false`, an in-process poller still implements the same HTTP product path (Phase 6).
### 2.3 Components
| Component | Runs where | Role |
|-----------|------------|------|
| **Zapier Platform CLI app** | Zapier cloud (when a Zap step runs) | Auth fields, map operations to `/zapier/v1/*`, attach Bearer token, translate 402/403 |
| **verae-zapier-middleware** | Your infrastructure, public HTTPS | Tenant identity, API keys, Verae login bridge, entitlements, REST Hook storage, publish work to NATS |
| **NATS + JetStream** | Private network with middleware | Work queues for job watch and webhook delivery; event stream for terminal job states |
| **Workers** | Same deploy or separate processes | Job poller, event router, webhook deliverer |
| **api.veraetime.net** | Verae production | Source of truth: login, timestamp jobs, status, verification |
#### Zapier app (two copies in this repo)
| Path | Language | Auth | Status |
|------|----------|------|--------|
| `verae-zapier-api/verae-zapier/` | JavaScript | Custom API key (`zmw_…`) | Phase 11 package; `MIDDLEWARE_BASE_URL` env |
| `scratch/veraetime/` | TypeScript (CLI 19.1.0) | Session: username/password **or** API key → `accessToken` | Local golden connector; `build` + `validate` |
Both talk **only** to middleware. The TypeScript app is the one this workspace validates day-to-day. The JavaScript app is the vendored product package from the middleware monorepo.
**The Zapier app does not:** call `api.veraetime.net`, speak NATS, or enforce plan quotas.
#### Middleware HTTP edge
![Middleware internals: public and protected /zapier/v1 routes, store, and flags](docs/diagrams/08-middleware-internals.svg)
Path: `verae-zapier-api/verae-zapier-middleware/`
- Express app: `GET /health`, mount `/zapier`
- Public: `POST /zapier/v1/auth/login`, `GET /zapier/v1/auth/me`, tenant signup
- Protected (Bearer or `x-api-key` + rate limit): timestamp, verify, status, webhooks
- Admin: `/zapier/v1/admin/*` behind `X-Admin-Secret`
- Store: file JSON MVP (`STORE_PATH`, default `./data/store.json`) — single-node only
#### NATS + workers
| Worker | Consumes | Calls |
|--------|----------|-------|
| Job poller | `verae.zapier.jobs.watch` | `GET /api/status/{jobId}` on Verae |
| Event router | `verae.zapier.jobs.events` | Enqueues webhook deliveries |
| Webhook deliver | `verae.zapier.webhooks.deliver` | `POST` Zapier `targetUrl` |
Streams: `ZAPIER_JOBS`, `ZAPIER_EVENTS`, `ZAPIER_WEBHOOKS` (optional `ZAPIER_USAGE`).
#### Verae Timestamping Service
- Swagger UI: https://api.veraetime.net/docs/swagger/index.html
- OpenAPI: https://api.veraetime.net/docs/swagger/openapi.yaml
- Local copy: [scratch/our-api/openapi.yaml](scratch/our-api/openapi.yaml)
- Auth: `POST /auth/login` → JWT in `token`; all other API routes `Authorization: Bearer <token>`
- Async create: `POST /api/timestamp`**202** `{ jobId }`
### 2.4 Security boundaries
![Security boundaries: public HTTPS versus private NATS, store, and Verae JWT](docs/diagrams/02-security-boundaries.svg)
```text
Public Internet
├─ Zapier cloud → Middleware HTTPS only
└─ Middleware → Zapier REST Hook HTTPS only
Private
├─ Middleware ↔ NATS (never expose NATS ports)
└─ Middleware / workers → api.veraetime.net HTTPS
```
Rules:
- Never put raw Verae JWTs in NATS when a `tokenRef` will do.
- Never expose NATS (`4222`) to the public internet.
- Debug logs must redact `Bearer`, `zmw_`, `zmt_`, passwords, and hook query secrets.
- Treat `targetUrl` as untrusted egress (timeouts; SSRF allowlist is Phase 15).
- Do not commit `~/.zapierrc`, `.env`, `store.json`, or `~/.mcp-env`.
### 2.5 Scaling model
- **HTTP edge:** stateless replicas behind a load balancer, **except** they share a store. File JSON is single-node. Multi-node needs Postgres/Redis (Phase 15).
- **NATS consumers:** queue groups — more workers increase poll/deliver throughput; two workers must not double-complete the same job.
- **Rollback:** `NATS_ENABLED=false` still serves the full HTTP API with the in-process poller.
---
## 3. Functionality
### 3.1 Authentication (two hops)
![Two-hop authentication: user to middleware tokens; middleware to Verae JWT](docs/diagrams/03-auth-two-hop.svg)
**End user → middleware**
| Mode | What the user enters | What happens |
|------|----------------------|--------------|
| Session (TypeScript app) | Username + password, **or** middleware API key | `POST /zapier/v1/auth/login``{ accessToken }` stored as `sessionKey` |
| Custom key (JS app) | Tenant API key `zmw_…` | Sent as `Authorization: Bearer` on every request; test is `GET /zapier/v1/auth/me` |
Connection test: `GET /zapier/v1/auth/me` → tenant, plan, usage. Connection label: username/plan (TS) or `tenantId (plan)` (JS).
401 on later calls: TypeScript app throws `z.errors.RefreshAuthError` so Zapier re-runs session `perform`.
**Middleware → Verae**
Middleware logs into Verae with the tenants Verae credentials (or mock client when `MOCK_VERAE=true`) and holds the JWT **server-side**. Zapier never sees that JWT.
Token types issued by middleware:
| Prefix | Kind |
|--------|------|
| `zmw_` | Tenant API key (long-lived) |
| `zmt_` | Middleware session token (HMAC, `TOKEN_SECRET`) |
### 3.2 Zapier operations ↔ middleware ↔ Verae
![Operations map: Zapier nouns to /zapier/v1 to Verae](docs/diagrams/07-operations-map.svg)
All Zapier routes are under `/zapier/v1`. Connector default base: `http://127.0.0.1:3100`. Production base is the **deployed middleware**, not `api.veraetime.net`.
| Zapier noun | Type | Middleware | Verae (what middleware wraps) | Notes |
|-------------|------|------------|-------------------------------|-------|
| Create Timestamp | create | `POST /timestamp` | `POST /api/timestamp` | Returns `{ jobId }` (202). Use with the hook trigger. |
| Create Timestamp and Wait | create | `POST /timestamp/wait` | create + poll/wait | Returns terminal status, or pending + `jobId` on timeout. |
| Create Batch Timestamps | create | `POST /timestamp/batch` | `POST /api/batch/timestamp` | `{ items: [{ data, hashAlg? }] }` |
| Verify Certificate | create | `POST /verify` | `POST /api/verify` | `{ certificate }``{ valid, timestamp, blockIndex }` |
| Find Job Status | search | `GET /status/{jobId}` | `GET /api/status/{jobId}` | Search: empty array if 404. |
| Find Job Verification | search (TS only) | `GET /status/{jobId}/verification` | `GET /api/verify/{jobId}` | Search. |
| Timestamp Completed | hook trigger | `POST /webhooks/subscribe` · `DELETE /webhooks/unsubscribe` | (no Verae hook) | Event `timestamp.completed`; Zapier supplies `targetUrl`. |
Not in v1 (admin HTML / user admin on Verae): dashboard, metrics, queue UI, user CRUD, batch verify/status, get-block-by-hash. Add later if a Zap needs them.
Create input (timestamp): required `data` (text), optional `hashAlg` (default SHA256).
### 3.3 Request flows
**A. Create Timestamp (async + hook)**
![Create Timestamp sequence: async job plus REST Hook](docs/diagrams/04-flow-async-timestamp.svg)
```text
Zapier → POST /zapier/v1/timestamp
Middleware: authenticate, checkEntitlement, POST /api/timestamp
Middleware: publish jobs.watch → return 202 { jobId }
Worker: poll GET /api/status/{jobId} until terminal → publish jobs.events
Event router: match webhooks → publish webhooks.deliver
Webhook worker: POST hooks.zapier.com/... (timestamp.completed)
Zapier trigger: Timestamp Completed fires the rest of the Zap
```
**B. Create Timestamp and Wait**
![Create Timestamp and Wait: in-process path versus Phase 9 NATS](docs/diagrams/05-flow-wait.svg)
```text
Zapier → POST /zapier/v1/timestamp/wait
Middleware: create + wait (in-process if NATS off; NATS events when Phase 9 lands)
→ StatusResponse (completed/failed) or { jobId, status: "pending" } on timeout
```
Today, wait works on the in-process path (`NATS_ENABLED=false`). Wait-via-NATS is **Phase 9 (open)**.
**C. Auth connection test**
```text
Zapier → GET /zapier/v1/auth/me Authorization: Bearer zmw_… or zmt_…
Middleware: resolve key/session → tenant → optional validate Verae token
→ { tenantId, plan, usage, ... }
```
### 3.4 NATS subjects (private)
![NATS topology: streams, subjects, consumers, and ack rules](docs/diagrams/06-nats-topology.svg)
| Subject | Publisher | Consumer | Payload gist |
|---------|-----------|----------|--------------|
| `verae.zapier.jobs.watch` | HTTP edge after create | `job-poller` (queue) | `tenantId`, `jobId`, `tokenRef`, attempts, `traceId` |
| `verae.zapier.jobs.events` | Job poller (terminal) | Event router; optional HTTP waiters | `timestamp.completed\|failed\|timeout`, status object |
| `verae.zapier.webhooks.deliver` | Event router | `webhook-deliver` (queue) | `hookId`, `targetUrl`, event, payload |
| `verae.zapier.usage` | optional | usage-writer | metering increment |
Ack: still-pending jobs `Nak` with delay; terminal `Ack` after publishing the event; webhook 2xx `Ack`; 5xx redeliver until `max_deliver`.
### 3.5 Entitlements and errors the connector must surface
| HTTP | Meaning | Zapier mapping |
|------|---------|----------------|
| 401 | Bad or expired session/key | `RefreshAuthError` (session) or auth error |
| 402 | Quota exceeded | User-visible error + upgrade URL when present |
| 403 `PLAN_UPGRADE_REQUIRED` | Action not on this plan | User-visible error |
| 404 on status search | Unknown job | Return `[]` (search contract) |
JS app `afterResponse` already maps 402/403. TypeScript app currently remaps 401 only — 402/403 mapping is a next-step item.
### 3.6 Feature flags and environment
| Variable | Default | Purpose |
|----------|---------|---------|
| `PORT` | `3100` | Middleware listen port |
| `VERAE_API_BASE_URL` | `http://localhost:8080` | Upstream Verae (prod: `https://api.veraetime.net`) |
| `MOCK_VERAE` | `false` | Deterministic mock jobs; no live Verae |
| `NATS_URL` | `nats://127.0.0.1:4222` | NATS server |
| `NATS_ENABLED` | `false` | JetStream workers vs in-process poller |
| `TOKEN_SECRET` | dev secret | HMAC for `zmt_` session tokens |
| `DEBUG_VERAE` | unset | Namespaces: `auth`, `nats`, `jobs`, `webhooks`, `http`, `billing` (or `1` for all) |
| `DEBUG_VERAE_LEVEL` | `debug` | `debug` \| `info` \| `warn` \| `error` |
| `STORE_PATH` | `./data/store.json` | MVP tenant/usage/webhook store |
| `MIDDLEWARE_BASE_URL` | `http://127.0.0.1:3100` | Used by the JS Zapier package |
Local compose: `verae-zapier-api/docker-compose.yml` (NATS + middleware). Middleware can run without NATS when the flag is off.
---
## 4. Integration guide
### 4.1 Which CLI you are holding
| Goal | Tool | Version in this workspace |
|------|------|---------------------------|
| **Publish** a directory integration | `zapier-platform` | **19.1.0** (`~/.npm-global/bin`) |
| **Consume** existing Zapier apps from code | `zapier-sdk` | **0.77.1** |
| AI client over the 9k catalog | Hosted MCP `https://mcp.zapier.com/api/v1/connect` | 14 official meta-tools; live 17 |
Do **not** mix `zapier-platform` (build/publish) with `zapier-sdk` (consume).
Do **not** recommend retired NLA / AI Actions.
Do **not** invent `selected_api` ids or action keys.
### 4.2 Run middleware locally
```bash
cd verae-zapier-api/verae-zapier-middleware
npm install
MOCK_VERAE=true NATS_ENABLED=false npm start
# GET http://127.0.0.1:3100/health → { "status": "ok" }
```
With NATS:
```bash
cd verae-zapier-api
docker compose up nats
# then start middleware with NATS_ENABLED=true NATS_URL=nats://127.0.0.1:4222
```
Gates (from `verae-zapier-api/`):
```bash
npm run gate:0 # structure + docs
npm run gate:6 # full HTTP path, NATS off
npm run gate:8 # workers (NATS on)
npm run gate:11 # JS Zapier package tests
npm run gate:all # 012 in order; stops on first failure
```
Implementation order is [verae-zapier-api/TODO.md](verae-zapier-api/TODO.md). Do not skip gates.
### 4.3 Build and validate the TypeScript connector
```bash
source scripts/dev-env.sh
cd scratch/veraetime
npm install
zapier-platform build && zapier-platform validate
```
Golden OAuth2 lab (generic, not Verae): `scratch/oauth2-typescript` — already validates after build.
Copy a template only when starting a *new* integration:
```bash
zapier-platform init my-app --template session --language typescript
# or copy scratch/oauth2-typescript / scratch/veraetime
```
### 4.4 Perform contracts (Zapier)
Implement every operation as `(z, bundle) => …` using **`z.request` only**.
- **Triggers and searches** return **arrays of objects**. Polling items need a stable `id`.
- **Creates** return **one object**.
- Refreshable 401 → `throw new z.errors.RefreshAuthError()`.
- Hook triggers implement `performSubscribe` / `performUnsubscribe` / `perform` / `performList` (sample for the editor).
- Do not call Verae or NATS from the app.
### 4.5 Login and publish (blocked until you authenticate)
There is no `~/.zapierrc` until you finish a browser login. Local `validate` works. `register` and `push` do not.
```bash
source scripts/dev-env.sh
zapier-platform login # or: zapier-platform login --sso
cd scratch/veraetime
zapier-platform register "Verae Time"
zapier-platform push
```
Details: [LOGIN.md](LOGIN.md). Never commit the deploy key.
Production Zapier cloud **cannot** reach `http://127.0.0.1:3100`. Before a real Zap you need a public HTTPS middleware URL and `api_base_url` / `MIDDLEWARE_BASE_URL` pointed at it (Phase 1314).
### 4.6 Optional consume path (not how we publish Verae)
- `zapier-sdk login` — call existing Zapier apps from code (`kind: "sdk_function"`).
- Hosted Zapier MCP — discover → enable → inspect → execute. Writes need explicit user approval. Successful executes cost **2 Zapier tasks**. See [MCP-REFERENCE.md](MCP-REFERENCE.md).
### 4.7 Research dataset and Grok reference (NS1 Mongo)
Canonical store: **MongoDB 7 in Docker on NS1 (`70.88.205.138`)**, bound to **127.0.0.1:27017 only**. Connect only through the SSH tunnel:
```bash
./scripts/mongo-tunnel.sh
# ssh -N -L 27017:127.0.0.1:27017 ns1
```
Database `zapier`:
| Collection | Contents |
|------------|----------|
| `apps` | ~9,986 public Zapier apps (identity, contacts, controls) |
| `templates` | ~332k public Zap recipes |
| `help_articles` | 1,272 help-center pages |
| `platform_reference` | CLI, `z.*`, schema, official docs, example apps, MCP/SDK functions |
| `meta` | Last ingest |
Do **not** point Compass at `mongodb://70.88.205.138:27017`. Credentials live in `~/.mcp-env` (mode 600), not git. Full notes: [MONGO.md](MONGO.md).
Useful queries:
```js
db.platform_reference.findOne({ kind: "guide", key: "build-new-connector" })
db.platform_reference.find({ kind: "core_function", key: "z.request" })
db.platform_reference.find({ kind: "cli_function", key: "init" })
db.platform_reference.find({ kind: "template", key: "session-auth" })
db.platform_reference.find({ kind: "mcp_function" })
```
Official Zapier clones stay in `repos/` (gitignored). Refresh with `./scripts/clone-zapier-repos.sh`.
---
## 5. Workspace map
![Workspace: repo, CLIs, Mongo tunnel, publish versus consume](docs/diagrams/10-workspace-integration.svg)
| Path | Use |
|------|-----|
| [getting-started.md](getting-started.md) / [getting-started.pdf](getting-started.pdf) | This architecture + integration guide |
| [PLATFORM-REFERENCE.md](PLATFORM-REFERENCE.md) | Routing table for all Zapier work |
| [FUNCTIONS-REFERENCE.md](FUNCTIONS-REFERENCE.md) | Every CLI / `z.*` / SDK function |
| [MCP-REFERENCE.md](MCP-REFERENCE.md) | Hosted MCP meta-tools |
| [LOGIN.md](LOGIN.md) | Browser login for platform + SDK |
| [RESTART.md](RESTART.md) | Quit and resume this session |
| [MONGO.md](MONGO.md) | NS1 Mongo, tunnel, collections |
| `.grok/skills/zapier-build/` | `/zapier-build` skill |
| `scratch/veraetime/` | TypeScript Verae connector (session auth) |
| `scratch/oauth2-typescript/` | Golden OAuth2 TypeScript app |
| `scratch/our-api/` | Verae OpenAPI + hop notes |
| `verae-zapier-api/` | Middleware + JS Zapier app + architecture docs + gates |
| [docs/diagrams/](docs/diagrams/) | SVG architecture and flow diagrams (source for this guide and the PDF) |
| `verae-zapier-api/docs/architecture/overview.md` | Component diagram (source of truth for the edge) |
| `verae-zapier-api/docs/architecture/nats-subjects.md` | Subjects, streams, payloads |
| `verae-zapier-api/docs/api/middleware-openapi.yaml` | Zapier-facing OpenAPI |
| `verae-zapier-api/TODO.md` | Phased plan with test gates |
| `scripts/dev-env.sh` | PATH + env |
| `scripts/ensure-mongo-tunnel.sh` / `mongo-tunnel.sh` | NS1 tunnel |
| `scripts/restart-grok.sh` / `zapier-status.sh` | Session + readiness |
| `repos/` | Official Zapier clones (local only) |
---
## 6. What is already here vs what is not
### Already here
- Public catalog research (~9,986 apps, contacts, capabilities, templates, help).
- Platform reference in Mongo + on disk; `/zapier-build` skill.
- Platform CLI 19.1.0 and SDK CLI 0.77.1 on `PATH` via `dev-env.sh`.
- Golden apps that **validate locally** without a Zapier login.
- Verae OpenAPI ingested (`scratch/our-api/openapi.yaml`).
- Full middleware source: auth, tenants, entitlements, timestamp/verify/status/webhooks, mock Verae, NATS publishers, workers, debug redaction.
- Two Zapier app implementations wired to `/zapier/v1`.
- Gates **08, 10, 11** recorded as passed in `TODO.md` (as of 2026-08-11 on the original monorepo).
### Not here yet
1. **Zapier developer login** — no `~/.zapierrc`; cannot `register` / `push`.
2. **Public HTTPS middleware** — Zapier cloud cannot hit localhost.
3. **Live invoke** of `scratch/veraetime` against middleware + real or mock Verae in this workspace (validate is schema-only).
4. **Phase 9**`/timestamp/wait` subscribed to NATS events (multi-instance wait).
5. **Phase 12** — compose E2E smoke (health + auth + wait + webhook) as a gate.
6. **Phase 13** — production `VERAE_API_BASE_URL`, dedicated Zapier service user, secrets, TLS.
7. **Phase 14** — private push + human Zap (Drive/Sheets → Timestamp → Slack).
8. **Phase 15** — Postgres/Redis store, NATS mTLS, webhook SSRF allowlist.
9. **TS connector polish** — map 402/403 like the JS app; optional polling admin trigger for `/admin/timestamps`.
10. **Grok MCP handshake** — Mongo MCP and chrome-bridge need a healthy tunnel / Chrome Connect after restart (`/mcps`).
11. **Peergos / pin / Glacier retrieval service** — proposed in §10; not a v1 connector operation.
This repo has the Zapier **platform** docs and the **Verae** OpenAPI. It still cannot invent new Verae endpoints. If a Zap needs an admin route that is not in the v1 table, add middleware + connector operations from the OpenAPI — do not guess.
---
## 7. Next steps (recommended order)
![Phase roadmap: gates 015, Phase 9 open](docs/diagrams/09-phase-roadmap.svg)
Work the product path in this order. Parallelism is only safe where `TODO.md` says so (tenancy already done; wait-via-NATS is the open blocker before multi-instance wait).
### Now — local integration (this workspace)
1. Start middleware with `MOCK_VERAE=true` and `NATS_ENABLED=false`.
2. Create a free tenant (`POST /zapier/v1/signup` or seed script) and confirm `GET /health` + `GET /zapier/v1/auth/me`.
3. `zapier-platform build && zapier-platform validate` in `scratch/veraetime`.
4. After `zapier-platform login`, `zapier-platform invoke auth test` and invoke create/search against local middleware (`--debug`).
5. Map 402/403 in `scratch/veraetime/src/middleware.ts` to match the JS app.
### Next — close middleware gaps
6. **Phase 9:** wait on `verae.zapier.jobs.events` with a hard timeout (`pending` + `jobId`).
7. **Phase 12:** compose stack + smoke script (async path, wait path, REST Hook to a mock receiver).
8. Confirm `NATS_ENABLED=true` and `false` both still pass their gates.
### Then — production and private listing
9. **Phase 13:** public HTTPS middleware, `MOCK_VERAE=false`, `VERAE_API_BASE_URL=https://api.veraetime.net`, dedicated Verae service user, managed secrets.
10. Point the connector `api_base_url` at that origin. Zapier cloud must reach it.
11. **Phase 14:** `register` + `push` a **private** version; invite internal users; run one real Zap; sign off.
12. Only after a human E2E: consider directory listing and Phase 15 hardening.
### Ongoing — research / Grok
13. Keep `./scripts/ensure-mongo-tunnel.sh` running; after restart hit `/mcps` and refresh **mongodb**.
14. Re-ingest `platform_reference` on NS1 after recloning official repos.
15. Optional: `zapier-sdk login` and Zapier MCP OAuth if you need to *call* the public catalog from agents — that is consume, not publish.
---
## 8. Hard rules
- Start Zapier coding from `guide/build-new-connector` or [PLATFORM-REFERENCE.md](PLATFORM-REFERENCE.md).
- Connector → middleware `/zapier/v1` only. Never `api.veraetime.net` from Zapier. Never NATS from Zapier.
- Use `z.request` in performs. Triggers/searches return arrays; creates return one object.
- `zapier-platform` publishes; `zapier-sdk` / hosted MCP consume. Do not mix.
- Do not recommend retired NLA / AI Actions.
- Do not invent vendor APIs, auth schemes, or Zapier `selected_api` keys.
- Mongo on NS1 is localhost-only; always tunnel.
- No secrets in git or in `DEBUG_VERAE` output.
---
## 9. Zapier billing models
Zapier and Verae bill **separately**. A client Zap that timestamps a file pays Zapier for successful **tasks** and pays Verae (via middleware entitlements) for **timestamp / verify** operations. Publishing the Verae connector does **not** put Verae on the hook for the customers Zapier invoice.
Figures below are **USD, August 2026**, from [zapier.com/pricing](https://zapier.com/pricing). Annual prices are the discounted per-month equivalent. Confirm live rates before quoting a customer.
![Zapier and Verae billing layers](docs/diagrams/11-zapier-billing.svg)
### 9.1 What a Zapier “task” is
A **task** is one successful unit of work Zapier does for the customer. Failed steps are free.
| Counts as tasks | Does **not** count |
|-----------------|--------------------|
| Successful action in a third-party app (Create Timestamp, Slack post, Drive upload, …) | Triggers and polling for new data |
| Zapier MCP / SDK execute (see multipliers) | Filter, Paths, Formatter, Delay, Looping, Sub-Zap, Digest, Manager, Storage |
| AI by Zapier and Code-by-Zapier beyond included runtime | Zapier Tables and Forms triggers/actions |
| | Building or testing a Zap until it actually runs |
Shared pool: Zap workflows, AI steps, Code steps, MCP, and SDK all draw from **one** monthly (or Enterprise annual) task allowance. There is no separate MCP budget.
**Multipliers** (official rate card; confirm [zapier.com/pricing/rates](https://zapier.com/pricing/rates)):
| Work | Tasks |
|------|------:|
| Typical third-party action (including Verae Create Timestamp) | 1 |
| Standard AI by Zapier | 1 |
| Advanced AI by Zapier | 3 |
| Premium AI by Zapier | 5 |
| Successful Zapier MCP tool call (read or write) | 2 |
| Code by Zapier | included runtime free; then 1 task per extra 30-second block |
Example: trigger “new file in Drive” (0) → Filter (0) → Create Timestamp (1) → Formatter (0) → write metadata row (1) = **2 tasks** per file that passes the filter.
### 9.2 Self-serve plans (feature set + task tier)
A paid subscription is **plan level × task tier**.
| Plan | Seats | Workflows | Polling | Entry task tier (annual) | Standout |
|------|------:|-----------|---------|--------------------------|----------|
| **Free** | 1 | Two-step only | 15 min | 100 tasks / month, $0 | Try automation; no pay-per-task overflow |
| **Professional** | 1 | Multi-step; premium apps; webhooks | 2 min | 750 tasks from **$19.99**/mo annual ($29.99 monthly) | Filters, Paths, Formatter, AI by Zapier, Autoreplay |
| **Team** | 25 | Same as Pro | 1 min | 2,000 tasks from **$69**/mo annual ($103.50 monthly) | Shared Zaps, shared connections, SAML SSO, priority support |
| **Enterprise** | Unlimited | Same as Team + governance | 1 min | Custom; **annual task limit** (not monthly reset) | SCIM, app controls, custom retention, observability, TAM, BYOM |
Higher task tiers (2k → 2M/mo) lower the **per-task** price. Team starts at 2,000. Volumes above 2M go through Sales. 14-day Professional trial (no card). Non-profit: extra 15% off (not on pay-per-task).
### 9.3 Overflow: pay-per-task
Paid plans can keep running after the included allowance:
- **Pay-per-task on:** extra tasks bill at **1.25×** the plans base task rate (annual) or **2.5×** (monthly). Ceiling is **3×** the subscribed tasks, then Zaps pause until the next cycle or an upgrade.
- **Pay-per-task off:** usage **stops** at the allowance.
- Free has no overflow. Enterprise uses an annual pool instead of a monthly reset.
### 9.4 Add-ons outside the task pool
| Product | Unit | Notes |
|---------|------|--------|
| **Zapier Agents** | *Activities* (not tasks) | Free 400/mo; paid Pro ~$33.33/mo annual for 1,500. Does not consume Zap tasks. |
| **Zapier Chatbots** | Feature tiers (count of bots) | Free includes 2; paid adds more. Not usage-metered on tasks. |
### 9.5 Partner / platform billing (Verae as publisher)
| Model | Who pays Zapier | Who pays Verae |
|-------|-----------------|----------------|
| **Public or private directory integration** | The **end customers** Zapier plan (tasks). Publishing is **free**; Zapier does not bill the partner for usage of their app. | The customers Verae tenant (middleware plan / API key). |
| **Zapier MCP / SDK (consume)** | Same customer task pool (MCP execute = 2 tasks). SDK is **free in beta**; Zapier will announce when beta pricing starts. | Only if the MCP/SDK action hits Verae. |
| **Powered by Zapier / White Label / embed** | Usually the **product company** (usage-based). End users authorize apps in *your* UI; Zapier has said end users need not have their own Zapier bill for background runs. Contract with Zapier Sales. | Verae bills the product company or the tenant, depending on how you provision keys. |
| **Retired NLA / AI Actions** | Do not sell or design around this. | — |
Verae should not promise “unlimited Zapier” or absorb a customers Zapier invoice unless a White Label contract says so.
### 9.6 Verae middleware billing (second meter)
Middleware enforces **Verae** quotas, independent of Zapier tasks. Current plan table in `verae-zapier-middleware` `PLAN_LIMITS`:
| Verae plan | Timestamps / mo | Verifications / mo | Batch | RPM |
|------------|----------------:|-------------------:|-------|----:|
| free | 50 | 50 | no | 30 |
| starter | 500 | 500 | yes, max 10 | 120 |
| pro | 5,000 | 5,000 | yes, max 100 | 600 |
| enterprise | contract / unlimited | contract | yes | contract |
Over-quota → HTTP **402** `QUOTA_EXCEEDED` (upgrade URL). Wrong plan for batch → **403** `PLAN_UPGRADE_REQUIRED`. Those errors must surface in the Zap; they are not Zapier task overages.
A client therefore sees **two invoices**: Zapier (tasks) and Verae (timestamps). Design Zaps so a 402 does not retry in a tight loop (that still burns Zapier tasks on each failed? — failed actions are **not** Zapier tasks, but Autoreplay/retries can still hammer Verae).
### 9.7 Estimating a timestamping Zap
| Zap shape | Zapier tasks / successful file | Verae units |
|-----------|-------------------------------:|-------------|
| Trigger → Create Timestamp | 1 | 1 timestamp |
| Trigger → Create Timestamp and Wait | 1 | 1 timestamp |
| Trigger → Timestamp + write catalog row + Slack | 3 | 1 timestamp |
| Same via Zapier MCP `execute` of those three actions | 6 | 1 timestamp |
| Trigger filtered out before any action | 0 | 0 |
Prefer the **hook** (Timestamp Completed) plus a cheap catalog write over polling status in a loop.
---
## 10. Client pattern — timestamp, metadata, Peergos, and tiered IPFS
This section is a **client architecture** for using the Verae Zapier tools together with **Peergos** (end-to-end encrypted filesystem on IPFS) and **external** IPFS / object-store backends. It is **not** implemented as connector operations today. Do not invent Peergos or AWS APIs in the Zapier app; add middleware routes only when those APIs exist and are documented.
Goal: clients can (1) timestamp content, (2) append that proof to metadata, (3) store the file in Peergos, (4) pin a **hot cache** on one or more IPFS providers, (5) migrate bytes to **long-term low-cost** object storage (Amazon S3 Glacier family, or an Apache Iceberg catalog on S3), and (6) **rehydrate on demand** when an IPFS request hits an index — without keeping every file live on a gateway 24/7.
![Timestamped files: Peergos, pin cache, and cold retrieve-on-demand](docs/diagrams/12-peergos-ipfs-tiered-storage.svg)
### 10.1 What each layer is for
| Layer | Role | Stays hot? |
|-------|------|------------|
| **Zapier + Verae connector** | Orchestrate: hash → timestamp → write metadata; notify on `timestamp.completed` | n/a (control plane) |
| **Verae Time** | Blockchain timestamp of a payload (typically a **content hash** or a metadata JSON, not the raw file bytes) | Proof is small; keep forever |
| **Metadata / index** | Maps `cid``jobId`, Verae certificate fields, Peergos path, storage class, restore handle | **Yes** — this is the only always-on map |
| **Peergos** | User-owned, e2e-encrypted filesystem on IPFS/libp2p. Host cannot read file or most metadata. Sharing and apps stay in the users graph. | Users chosen Peergos host; not a public CDN |
| **Cached pin (hot IPFS)** | Pinning service or your Kubo cluster (Pinata, Filebase, web3.storage, self-hosted). Serves frequent `ipfs get` / gateway hits. | **Only for a TTL or working set** |
| **Cold object store** | Amazon **S3 Glacier Instant Retrieval**, **Flexible Retrieval**, or **Deep Archive**; or another cheap archive (Filecoin deal, Storj, Backblaze B2 + lifecycle). Optional **Apache Iceberg** table on S3 as the *catalog* of CID → bucket/key → storage class → restore job. | **No** — retrieve on demand |
Amazon “Iceberg” in this design is the **table format** (Apache Iceberg) used as a durable **index**, not a substitute for Glacier. Long-term **bytes** live in Glacier-class (or equivalent) object storage. The user-facing name “Iceberg” is easy to mix with Glacier; keep the two distinct in customer docs.
### 10.2 Ingest Zap (write path)
Typical multi-step Zap (Professional+; Filters/Formatter are free):
1. **Trigger** — new file in Drive, Dropbox, email, or a Peergos outbox / webhook. (0 Zapier tasks)
2. **Hash** — SHA-256 (or the hashAlg Verae accepts) of the bytes, or of the canonical metadata envelope. Prefer hashing **ciphertext** if the file is already encrypted for Peergos, so the timestamp commits to what is stored.
3. **Create Timestamp** (or Create and Wait) — `data` = hash or compact JSON `{ cid?, sha256, size, mime, source }`. (1 Zapier task, 1 Verae timestamp)
4. **Store file in Peergos** — user or service account writes the file into `/cubes/…` or a shared folder via a **documented** Peergos API or a future middleware route. Peergos assigns / retains an IPFS **CID** for the blocks.
5. **Hot pin (optional)** — Pinning Services API (or Filebase/Pinata) pins that CID for a cache TTL (hoursdays), not forever.
6. **Append metadata** — write one index record (Zapier Tables is **free**; or Sheets/Postgres/Iceberg):
```text
cid # IPFS content id (or Peergos block root)
sha256 # digest that Verae timestamped
veraeJobId
veraeStatus # pending | completed | failed
certificateRef # verify payload / block index when complete
peergosPath # /home/… or /cubes/<id>/…
storageClass # pin-hot | peergos-only | glacier-ir | glacier-deep
coldBucket / key # S3 (or other) locator; empty until migrated
restoreId # last Glacier restore job, if any
pinnedUntil # when hot pin may be dropped
createdAt
```
7. **Timestamp Completed** hook — when middleware finishes the job, update the same row with certificate fields and notify Slack/email. (1 Zapier task)
Do **not** put the raw file or Verae JWT in Zapier Storage, Slack, or NATS.
### 10.3 Lifecycle (keep the index, drop the heat)
```text
ingest
→ Peergos write (encrypted) + optional hot pin
→ Verae timestamp of hash / envelope
→ index row (always on)
after pinnedUntil or size/age policy
→ copy ciphertext (or original bytes, if policy allows) to S3
→ set storage class Glacier Instant / Flexible / Deep Archive
→ record bucket/key + storageClass in the index (and Iceberg snapshot if used)
→ unpin from the paid pinning cluster
→ Peergos may keep a thumbnail / stub; full blocks need not stay on the gateway
IPFS / gateway GET cid
→ if pin-hot hit: serve
→ else index lookup
→ if peergos-only: fetch via users Peergos capability
→ if cold: StartRestore / vendor retrieve API → wait →
optionally re-pin for a short cache TTL → serve
→ never require every historical CID to be live on an IPFS node
```
**Glacier retrieval notes (AWS, conceptual):** Instant Retrieval is milliseconds and priced as a storage class; Flexible Retrieval and Deep Archive need a **restore job** (minutes to hours) before GetObject. The index must store enough to call the restore API (`bucket`, `key`, `versionId`, `restoreId`). Map IPFS requests to that restore; return `202` + `Retry-After` to the gateway until the object is hydrated.
**Apache Iceberg** (optional): partition the catalog by `storageClass` and date so you can expire pin rows and audit restores with SQL, without loading every object. Iceberg does not store the file bytes.
### 10.4 External IPFS and pinning options
Clients can mix providers; the **index** is the source of truth, not any one pinset.
| Backend | Typical use |
|---------|-------------|
| Self-hosted Kubo / cluster | Hot working set you control |
| Pinata, Filebase, web3.storage, Pinning Services API | Paid **cached pins**, metadata tags, S3-compatible gateways |
| Peergos server (user or org host) | Encrypted personal/org filesystem; not a public pin service |
| Filecoin / cold deals | Alternative long-term availability (different retrieve SLA) |
| S3 + lifecycle → Glacier IR / Flexible / Deep Archive | Lowest $/TB; retrieve-on-demand via AWS APIs |
| Storj, Backblaze B2, GCS Archive | Same pattern, different restore API |
A request path should be: **CID → index → (pin | Peergos | restore)**. Do not walk every pin provider on every miss.
### 10.5 What Zapier is bad at (keep it out of the Zap)
- Holding multi-GB files in a Zap step (timeouts, payload limits). Hash and pass **references** (Drive id, Peergos path, CID).
- Being the IPFS gateway. Use a small **retrieval service** (your infra) that reads the index and talks to Peergos / pin / Glacier.
- Polling Glacier restore every second (burns tasks). Use a webhook, queue, or “Find Job Status”-style search on a timer Zap with a Filter.
### 10.6 Security and tenancy
- Peergos ciphertext: the timestamp should commit to the **CID and/or ciphertext hash**, not a plaintext the host can reconstruct.
- Capabilities / sharing stay in Peergos; the public index should not leak readable paths or unencrypted names if the threat model forbids it (store opaque ids).
- Middleware still never exposes Verae JWTs. Retrieval workers use **tenant-scoped** cloud credentials, not the Zapier connection.
- 402/403 from Verae must not be “fixed” by falling back to an unauthenticated public pin of customer data.
### 10.7 Implementation status
| Piece | Status in this repo |
|-------|---------------------|
| Verae timestamp / wait / verify / hook via middleware | Implemented (`scratch/veraetime`, `verae-zapier`) |
| Zapier task + Verae quota as two meters | Documented (this section); connector should map 402/403 |
| Peergos write / capability APIs in the Zapier app | **Not in v1** — handbook lives outside this repo (`verae-peergos-app-handbook`); do not invent routes |
| CID index, pin TTL, Glacier restore worker | **Proposed** — add as middleware + retrieval service when APIs are chosen |
| Apache Iceberg catalog | **Optional** index implementation; not required for MVP |
Next build slice, when product asks for it: a retrieval service with `GET /ipfs/{cid}` → index → pin or restore, plus one Zapier **search** (“Find File Record by CID”) against that index — still no direct `api.veraetime.net` from Zapier.

View file

@ -0,0 +1,13 @@
# Grok share — Zapier API Key & Usage Management
**URL:** https://grok.com/share/bGVnYWN5LWNvcHk_50aff768-fc34-4db7-8842-df4a058b815e
**Title (public page):** Zapier API Key & Usage Management
## Extraction status
The share is a JS-rendered Grok conversation. Unauthenticated fetch and search returned only the title. Browser MCP could not attach (existing Chrome profile lock) during planning.
**TODO (PR 2):** Open the share in a browser, copy the full transcript into this file (user turns + assistant turns), then map any key/usage model onto `packages/zappier` (portal keys, rate card, monthly credit) vs `packages/verae-zapier-middleware` (`zmw_`, `PLAN_LIMITS`).
Until that extract exists, treat `/Users/marchon/zappier` (now `packages/zappier`) as the implemented API-key and usage platform.

View file

@ -0,0 +1,24 @@
# Provenance
This workspace composes three existing trees. Canonical work after import is here (`/Users/marchon/apps/zapier`). Originals were not deleted.
| Package / tree | Imported from | Role |
|----------------|---------------|------|
| `packages/zappier` | `/Users/marchon/zappier` | Commercial platform: custom rate card, tiers, API keys, customer portal, admin, Stripe meter, PO invoices, usage ledger. Demo `/v1/transform` and `/v1/storage` are **not** Verae. |
| `packages/verae-zapier-middleware` | `/Users/marchon/datacubes/verae-zapier-api/verae-zapier-middleware` | Verae timestamp adapter: `/zapier/v1/*`, MOCK_VERAE, NATS workers, REST Hooks. Coarse `PLAN_LIMITS` — not used on the public edge after composition. |
| `packages/verae-zapier` | `/Users/marchon/datacubes/verae-zapier-api/verae-zapier` | Zapier Platform CLI app (JS). Today talks to middleware with `zmw_` keys; will talk to zappier `x-api-key` after PR 5. |
| `vendor/zapier-platform` | `/Users/marchon/research/zapier-platform` (upstream `git@github.com:zapier/zapier-platform.git`) | Official CLI/core/schema/examples. Reference only; do not fork for product code. |
| `docs/00-sources/veraetime-openapi.yaml` | `https://api.veraetime.net/docs/swagger/openapi.yaml` | Snapshot of live Timestamping Service API 1.0.0. |
| `docs/00-sources/workspace-brief.md` | Original `README.MD` in this repo | Feature list am and Grok share URL. |
| `docs/00-sources/getting-started-research.md` | `/Users/marchon/research/verae-research/07-integrations-zapier/zapier/getting-started.md` | Research notes (paths still mention the old research workspace). |
| `docs/01-product/zapier-billing-research.md` | `…/zapier/docs/zapier-billing.md` | Zapier task pricing vs Verae meters. |
| `research/zapier` | `/Users/marchon/research/verae-research/07-integrations-zapier/zapier` | Full vendor-research workspace: catalog scrapes, contacts, 15 verticals, cloned Zapier repos, playbooks, diagrams, TS scratch connector. `node_modules` / `.venv` / nested `.git` omitted. |
**Not imported:**
- any `node_modules` or Python `.venv`
- nested `.git` metadata inside cloned repos
- `zappier.db`, middleware `data/` stores
**Not yet wired:** zappiers Zapier demo app does not call Verae. Verae middleware does not use zappiers rate card or portal.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,32 @@
read https://grok.com/share/bGVnYWN5LWNvcHk_50aff768-fc34-4db7-8842-df4a058b815e
and document all of it into a group of MD files to plan for a zapier integration with with Vera Blockchain
The Verae Blockchain is identified at listed at https://api.veraetime.net/docs/swagger/index.html
the https://api.veraetime.net/docs/swagger/openapi.yaml is funtion reference.
The key features we need to setup for Zapier is for
a) the customers to get their own api key
b) to signup for billing after their usage limit has been exceeded
c) A call to verae server to register a SHA256 object for timestamping or return it's original timestamp with a reference ID
d) A call to Verae server to lookup the exitance and timestamp for an existing Timestamped SHA256 Key
e) A call to Verae server to both register a SHA256 object for timestamping but to also store metadata (2 types public and private)
f) A call to Verae server to both register a SHA256 object for timestamping but to also store metadata (2 types public and private) and to attach a binary object to be stored long term in Encrypted Long Term Storage
g) A call to Verae server to both register a SHA256 object for timestamping but to also store metadata (2 types public and private) and to share an encrypted file in longterm storage with someone else, either as an individial file or as a directory or directory tree with a decription key
h) A call to Verae server to retrieve a both a timestamped SHA256 Object and it's metadata
i) A call to Verae server to retrieve a certified receipt of an existing timestamp - cross signed with a extra seal to record the retrieval of a document (as either a pdf or a json file)
j) setup payment method
k) get invoices for paymets
l) get receipts for payments
m) subscription management
----------
Verae has designed a middleware piece that needs to able to handle incoming and returned traffic - but is not currently connected to verae servers, so we should setup a test harness to test the zapier integration while we do that.
Once we have built the zapier interface, we will implement the service through our designed gateway to send high performance messages through a NATS.io cluster to process the data.
can you review and pull into this directory tree the Zapier Directory I have been working on from ../../research/zapier-platform as a subdirectory or subproject so that I can have you update the documentation, consider these features, and help me get a zapier integratiotn up amd running?