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.
729 lines
38 KiB
Markdown
729 lines
38 KiB
Markdown
# 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
|
||
|
||

|
||
|
||
```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
|
||
|
||

|
||
|
||
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
|
||
|
||

|
||
|
||
```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)
|
||
|
||

|
||
|
||
**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 tenant’s 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
|
||
|
||

|
||
|
||
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)**
|
||
|
||

|
||
|
||
```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**
|
||
|
||

|
||
|
||
```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)
|
||
|
||

|
||
|
||
| 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 # 0–12 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 13–14).
|
||
|
||
### 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
|
||
|
||

|
||
|
||
| 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 **0–8, 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)
|
||
|
||

|
||
|
||
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 customer’s 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.
|
||
|
||

|
||
|
||
### 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 plan’s 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 customer’s** Zapier plan (tasks). Publishing is **free**; Zapier does not bill the partner for usage of their app. | The customer’s 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 customer’s 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.
|
||
|
||

|
||
|
||
### 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 user’s graph. | User’s 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 (hours–days), 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 user’s 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.
|