2518 lines
80 KiB
HTML
2518 lines
80 KiB
HTML
<!DOCTYPE html>
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="generator" content="pandoc" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||
<title>dev-overview — NATS routes and developer credentials</title>
|
||
<style>
|
||
/* Default styles provided by pandoc.
|
||
** See https://pandoc.org/MANUAL.html#variables-for-html for config info.
|
||
*/
|
||
html {
|
||
color: #1a1a1a;
|
||
background-color: #fdfdfd;
|
||
}
|
||
body {
|
||
margin: 0 auto;
|
||
max-width: 36em;
|
||
padding-left: 50px;
|
||
padding-right: 50px;
|
||
padding-top: 50px;
|
||
padding-bottom: 50px;
|
||
hyphens: auto;
|
||
overflow-wrap: break-word;
|
||
text-rendering: optimizeLegibility;
|
||
font-kerning: normal;
|
||
}
|
||
@media (max-width: 600px) {
|
||
body {
|
||
font-size: 0.9em;
|
||
padding: 12px;
|
||
}
|
||
h1 {
|
||
font-size: 1.8em;
|
||
}
|
||
}
|
||
@media print {
|
||
html {
|
||
background-color: white;
|
||
}
|
||
body {
|
||
background-color: transparent;
|
||
color: black;
|
||
font-size: 12pt;
|
||
}
|
||
p, h2, h3 {
|
||
orphans: 3;
|
||
widows: 3;
|
||
}
|
||
h2, h3, h4 {
|
||
page-break-after: avoid;
|
||
}
|
||
}
|
||
p {
|
||
margin: 1em 0;
|
||
}
|
||
a {
|
||
color: #1a1a1a;
|
||
}
|
||
a:visited {
|
||
color: #1a1a1a;
|
||
}
|
||
img {
|
||
max-width: 100%;
|
||
}
|
||
svg {
|
||
height: auto;
|
||
max-width: 100%;
|
||
}
|
||
h1, h2, h3, h4, h5, h6 {
|
||
margin-top: 1.4em;
|
||
}
|
||
h5, h6 {
|
||
font-size: 1em;
|
||
font-style: italic;
|
||
}
|
||
h6 {
|
||
font-weight: normal;
|
||
}
|
||
ol, ul {
|
||
padding-left: 1.7em;
|
||
margin-top: 1em;
|
||
}
|
||
li > ol, li > ul {
|
||
margin-top: 0;
|
||
}
|
||
blockquote {
|
||
margin: 1em 0 1em 1.7em;
|
||
padding-left: 1em;
|
||
border-left: 2px solid #e6e6e6;
|
||
color: #606060;
|
||
}
|
||
code {
|
||
font-family: Menlo, Monaco, Consolas, 'Lucida Console', monospace;
|
||
font-size: 85%;
|
||
margin: 0;
|
||
hyphens: manual;
|
||
}
|
||
pre {
|
||
margin: 1em 0;
|
||
overflow: auto;
|
||
}
|
||
pre code {
|
||
padding: 0;
|
||
overflow: visible;
|
||
overflow-wrap: normal;
|
||
}
|
||
.sourceCode {
|
||
background-color: transparent;
|
||
overflow: visible;
|
||
}
|
||
hr {
|
||
border: none;
|
||
border-top: 1px solid #1a1a1a;
|
||
height: 1px;
|
||
margin: 1em 0;
|
||
}
|
||
table {
|
||
margin: 1em 0;
|
||
border-collapse: collapse;
|
||
width: 100%;
|
||
overflow-x: auto;
|
||
display: block;
|
||
font-variant-numeric: lining-nums tabular-nums;
|
||
}
|
||
table caption {
|
||
margin-bottom: 0.75em;
|
||
}
|
||
tbody {
|
||
margin-top: 0.5em;
|
||
border-top: 1px solid #1a1a1a;
|
||
border-bottom: 1px solid #1a1a1a;
|
||
}
|
||
th {
|
||
border-top: 1px solid #1a1a1a;
|
||
padding: 0.25em 0.5em 0.25em 0.5em;
|
||
}
|
||
td {
|
||
padding: 0.125em 0.5em 0.25em 0.5em;
|
||
}
|
||
header {
|
||
margin-bottom: 4em;
|
||
text-align: center;
|
||
}
|
||
#TOC li {
|
||
list-style: none;
|
||
}
|
||
#TOC ul {
|
||
padding-left: 1.3em;
|
||
}
|
||
#TOC > ul {
|
||
padding-left: 0;
|
||
}
|
||
#TOC a:not(:hover) {
|
||
text-decoration: none;
|
||
}
|
||
code{white-space: pre-wrap;}
|
||
span.smallcaps{font-variant: small-caps;}
|
||
div.columns{display: flex; gap: min(4vw, 1.5em);}
|
||
div.column{flex: auto; overflow-x: auto;}
|
||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||
/* The extra [class] is a hack that increases specificity enough to
|
||
override a similar rule in reveal.js */
|
||
ul.task-list[class]{list-style: none;}
|
||
ul.task-list li input[type="checkbox"] {
|
||
font-size: inherit;
|
||
width: 0.8em;
|
||
margin: 0 0.8em 0.2em -1.6em;
|
||
vertical-align: middle;
|
||
}
|
||
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
|
||
/* CSS for syntax highlighting */
|
||
html { -webkit-text-size-adjust: 100%; }
|
||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||
.sourceCode { overflow: visible; }
|
||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||
div.sourceCode { margin: 1em 0; }
|
||
pre.sourceCode { margin: 0; }
|
||
@media screen {
|
||
div.sourceCode { overflow: auto; }
|
||
}
|
||
@media print {
|
||
pre > code.sourceCode { white-space: pre-wrap; }
|
||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||
}
|
||
pre.numberSource code
|
||
{ counter-reset: source-line 0; }
|
||
pre.numberSource code > span
|
||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||
pre.numberSource code > span > a:first-child::before
|
||
{ content: counter(source-line);
|
||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||
border: none; display: inline-block;
|
||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||
-khtml-user-select: none; -moz-user-select: none;
|
||
-ms-user-select: none; user-select: none;
|
||
padding: 0 4px; width: 4em;
|
||
color: #aaaaaa;
|
||
}
|
||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||
div.sourceCode
|
||
{ }
|
||
@media screen {
|
||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||
}
|
||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||
code span.at { color: #7d9029; } /* Attribute */
|
||
code span.bn { color: #40a070; } /* BaseN */
|
||
code span.bu { color: #008000; } /* BuiltIn */
|
||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||
code span.ch { color: #4070a0; } /* Char */
|
||
code span.cn { color: #880000; } /* Constant */
|
||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||
code span.dt { color: #902000; } /* DataType */
|
||
code span.dv { color: #40a070; } /* DecVal */
|
||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||
code span.ex { } /* Extension */
|
||
code span.fl { color: #40a070; } /* Float */
|
||
code span.fu { color: #06287e; } /* Function */
|
||
code span.im { color: #008000; font-weight: bold; } /* Import */
|
||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||
code span.op { color: #666666; } /* Operator */
|
||
code span.ot { color: #007020; } /* Other */
|
||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||
code span.ss { color: #bb6688; } /* SpecialString */
|
||
code span.st { color: #4070a0; } /* String */
|
||
code span.va { color: #19177c; } /* Variable */
|
||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header id="title-block-header">
|
||
<h1 class="title">dev-overview — NATS routes and developer
|
||
credentials</h1>
|
||
</header>
|
||
<h1 id="dev-overview">dev-overview</h1>
|
||
<p>Developer map of the Verae Time × Zapier <strong>NATS message
|
||
fabric</strong>: what was designed, what is actually running on NS1, how
|
||
a Zapier timestamp job reaches <code>api.veraetime.net</code> (or the
|
||
mock) and comes back, and what is still unwired.</p>
|
||
<p><strong>Forgejo:</strong>
|
||
https://git.georgelambert.org/marchon/dev-overview<br />
|
||
<strong>SSH:</strong>
|
||
<code>ssh://git@git.georgelambert.org:2223/marchon/dev-overview.git</code><br />
|
||
<strong>Catalog:</strong>
|
||
https://zapier.georgelambert.org/packages/dev-overview/<br />
|
||
<strong>Date:</strong> 2026-09-14</p>
|
||
<p>Zapier cloud <strong>never</strong> connects to NATS. It talks HTTPS
|
||
to <code>https://zap.zapier.georgelambert.org</code> (access-zapier)
|
||
then middleware. <code>api.veraetime.net</code> is <strong>HTTPS
|
||
only</strong> — middleware is the NATS client.</p>
|
||
<h2 id="contents">Contents</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 45%" />
|
||
<col style="width: 54%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Doc</th>
|
||
<th>What</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="00-sources.md">00-sources.md</a></td>
|
||
<td>Every other doc that names a NATS address or TCP route</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="01-nats-routes.md">01-nats-routes.md</a></td>
|
||
<td>Transport ports + every <code>verae.*</code> address: designed vs
|
||
running</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="02-endpoint-io.md">02-endpoint-io.md</a></td>
|
||
<td>Per-address: purpose, inbound from, outbound to, how to connect
|
||
Verae API</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="03-zapier-verae-path.md">03-zapier-verae-path.md</a></td>
|
||
<td>Zapier → middleware → Verae timestamp server → poll → events →
|
||
Zapier</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="04-gaps-and-finish.md">04-gaps-and-finish.md</a></td>
|
||
<td>Modules not connected; billing/event/aux; ordered finish list</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a
|
||
href="05-developer-credentials.md">05-developer-credentials.md</a></td>
|
||
<td>URLs, tokens, lab API keys, staff/IAM, git, env cheat sheet</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="cluster-running">Cluster (running)</h2>
|
||
<pre class="text"><code>nats://10.10.10.21:4222,nats://10.10.10.22:4222,nats://10.10.10.23:4222</code></pre>
|
||
<p>JetStream streams (replicas=3, file): <code>ZAPIER_JOBS</code>,
|
||
<code>ZAPIER_EVENTS</code>, <code>ZAPIER_WEBHOOKS</code>,
|
||
<code>ZAPIER_USAGE</code>, <code>VERAE_ARCHIVE</code>.</p>
|
||
<p>Public token-gated <strong>client</strong> edge (not cluster routes):
|
||
<code>nats://<token>@70.88.205.138:4222</code>,
|
||
<code>wss://nats.zapier.georgelambert.org</code>. Do not put this in
|
||
Zapier.</p>
|
||
<h1 id="sources-other-documentation-that-names-routes">Sources (other
|
||
documentation that names routes)</h1>
|
||
<p>This inventory is compiled from code and from these docs. Do not
|
||
invent extra <code>verae.*</code> names.</p>
|
||
<h2 id="master-maps">Master maps</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 27%" />
|
||
<col style="width: 72%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Path</th>
|
||
<th>What it names</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>packages/docs-master/modules-and-nats.md</code></td>
|
||
<td>Module catalog, subject table, HTTPS vs NATS</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/docs-master/MESSAGE-FLOWS.md</code></td>
|
||
<td>Create/wait/includeAttached/batch Merkle</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/docs-master/archive-nats.md</code></td>
|
||
<td>Archive put/query/reply</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>docs/02-architecture/nats-subjects.md</code></td>
|
||
<td><code>verae.zapier.*</code> payloads + streams</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>docs/02-architecture/modules-and-nats.md</code></td>
|
||
<td>Same catalog as docs-master</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>docs/02-architecture/overview.md</code></td>
|
||
<td>Poller / events / webhooks</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/overview/03-nats-cluster.md</code></td>
|
||
<td>Cluster URLs, who may connect</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/overview/06-address-routing.md</code></td>
|
||
<td>Address pattern + plane mapping</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/verae-ops/06-linking-services.md</code></td>
|
||
<td>How services attach</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/verae-nats-cluster/scripts/ensure-streams.sh</code></td>
|
||
<td>Live stream create</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/verae-nats-cluster/conf/nats.conf.tmpl</code></td>
|
||
<td><code>:4222</code> / <code>:6222</code> / <code>:7422</code> /
|
||
monitor <code>:8222</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/verae-nats-cluster/conf/public-edge.conf.tmpl</code></td>
|
||
<td>Public leaf <code>:4222</code>, WS <code>:14422</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/verae-fleet/overlays/ns1/fleet.json</code></td>
|
||
<td>Enabled vs in-process workers</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/verae-keep</code>
|
||
<code>units.px-worker.json</code></td>
|
||
<td>Worm/tree on LXC 510</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/verae-zapier-middleware/src/nats/subjects.js</code></td>
|
||
<td>Product subjects + consumers</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/zappier-account-balance/src/subjects.js</code></td>
|
||
<td>Billing subjects</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/verae-access-authz/src/subjects.js</code> +
|
||
<code>policy.js</code></td>
|
||
<td>Authz + plane allow lists</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>packages/zappier/src/billing-nats.ts</code></td>
|
||
<td>Edge billing client</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2
|
||
id="per-package-nats.md-forgejo-module-copies-also-live-under-docs-mastermodules">Per-package
|
||
<code>NATS.md</code> (Forgejo module copies also live under
|
||
<code>docs-master/modules/</code>)</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 45%" />
|
||
<col style="width: 55%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Package</th>
|
||
<th>Addresses</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>verae-zapier-middleware</code></td>
|
||
<td><code>jobs.watch</code> out, <code>jobs.events</code> in,
|
||
archive.query via aggregator</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-jobs-events</code></td>
|
||
<td><code>jobs.events</code>; exclusive →
|
||
<code>verae.internal.jobs.events</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-request-splitter</code></td>
|
||
<td><code>archive.put</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-archive-worm</code></td>
|
||
<td><code>archive.put</code> / <code>query</code> /
|
||
<code>reply.<id></code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-tree-node</code></td>
|
||
<td>same, <code>kind: tree</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-archive-aggregator</code></td>
|
||
<td><code>query</code> out, <code>reply.<id></code> in</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>zappier-account-balance</code></td>
|
||
<td><code>verae.billing.*</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>zappier</code> (edge)</td>
|
||
<td>package <code>NATS.md</code> is stale (“does not publish”);
|
||
<strong>code</strong> publishes usage/payment/credit/customer.put when
|
||
<code>NATS_URL</code> is set. Use
|
||
<code>docs-master/modules/zappier/NATS.md</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>zappier-customer-service</code></td>
|
||
<td><code>statement.get</code>, <code>balance.adjust</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>zappier-sales-pricing</code></td>
|
||
<td><code>statement.get</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>zappier-accounting-export</code></td>
|
||
<td><code>statement.get</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>zappier-identity</code></td>
|
||
<td><code>identity.bind</code> / <code>lookup</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-access-authz</code></td>
|
||
<td><code>authz.check</code> / <code>authz.deny</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-access-web</code></td>
|
||
<td><code>access.web.in</code> / <code>access.web.billing.*</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-access-api</code></td>
|
||
<td><code>access.api.*</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-access-zapier</code></td>
|
||
<td><code>access.zapier.*</code> then HTTPS to middleware</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-access-leaf</code></td>
|
||
<td><code>access.leaf.in</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-access-staff</code></td>
|
||
<td><code>access.staff.*</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-nats-process</code></td>
|
||
<td><code>verae.example.process.*</code> (template)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-nats-accounts</code></td>
|
||
<td>policy only (INTERNAL vs LEAF)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-zapier-simulator</code></td>
|
||
<td>in-process replay of product addresses</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-keep</code> / <code>verae-fleet</code> /
|
||
<code>verae-uptime</code> / <code>verae-backup</code></td>
|
||
<td>no product subjects</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Simulator <code>src/pipeline.js</code> and
|
||
<code>src/monitors.js</code> list the same product addresses for traces;
|
||
they are not live subscribers.</p>
|
||
<h1 id="designed-vs-running-nats-routes">Designed vs running NATS
|
||
routes</h1>
|
||
<p><strong>Designed</strong> = named in docs or in code.<br />
|
||
<strong>Running</strong> = a process on NS1 is subscribed or publishing
|
||
it <strong>today</strong>, and (for durable work) a JetStream stream
|
||
exists.</p>
|
||
<p>Cluster name <code>verae</code>. Internal clients use the three LXC
|
||
on <code>vmbr1</code>. Public edge is a <strong>leaf</strong>, not a
|
||
second cluster.</p>
|
||
<p>Legend: <strong>JS</strong> = JetStream stream; <strong>RR</strong> =
|
||
core request-reply; <strong>pub</strong> = core or JS fire-and-forget;
|
||
<strong>Q</strong> = queue group.</p>
|
||
<h2 id="transport-tcp-leaf-public">Transport (TCP / leaf / public)</h2>
|
||
<p>These are NATS <strong>server</strong> routes, not
|
||
<code>verae.*</code> subjects.</p>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 29%" />
|
||
<col style="width: 29%" />
|
||
<col style="width: 26%" />
|
||
<col style="width: 14%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Endpoint</th>
|
||
<th>Designed</th>
|
||
<th>Running</th>
|
||
<th>Who</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>10.10.10.21–23:4222</code> client</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>middleware, access-*, billing, worm/tree, identity,
|
||
CS/sales/acct</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>10.10.10.21–23:6222</code> cluster routes</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong> (private <code>vmbr1</code> only)</td>
|
||
<td>nats-a/b/c to each other</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>10.10.10.21–23:7422</code> leaf hub</td>
|
||
<td>yes (public-edge)</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>public edge remotes</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>127.0.0.1:8222</code> monitor on each LXC</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>operators on the guest</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>70.88.205.138:4222</code> public client</td>
|
||
<td>yes (token)</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>leaf edge; <strong>not</strong> Zapier</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>127.0.0.1:14422</code> WS on edge</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>Caddy → <code>wss://nats.zapier.georgelambert.org</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>127.0.0.1:8229</code> edge monitor</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>host loopback</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>:6222</code> / <code>:7422</code> / <code>:8222</code> on
|
||
<code>vmbr0</code></td>
|
||
<td><strong>no</strong></td>
|
||
<td><strong>must stay closed</strong></td>
|
||
<td>—</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Host leftover <code>127.0.0.1:4222</code> (docker-proxy) +
|
||
<code>10.10.10.1:4222</code> socat</td>
|
||
<td>lab leftover</td>
|
||
<td>present</td>
|
||
<td><strong>do not</strong> point product <code>NATS_URL</code>
|
||
here</td>
|
||
</tr>
|
||
<tr>
|
||
<td>MQTT <code>:1883</code></td>
|
||
<td>study only</td>
|
||
<td><strong>removed</strong></td>
|
||
<td>—</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Internal keep/fleet/middleware URL:</p>
|
||
<pre class="text"><code>nats://10.10.10.21:4222,nats://10.10.10.22:4222,nats://10.10.10.23:4222</code></pre>
|
||
<p>Public (token in <code>/etc/nats/public-edge.token</code> on NS1):
|
||
<code>nats://<token>@70.88.205.138:4222</code>. Isolation on the
|
||
hub is still <code>$G</code> + private LAN. nkeys/mTLS sketched in
|
||
<code>verae-nats-accounts</code>, <strong>not</strong> flipped.</p>
|
||
<h2 id="jetstream-streams-running">JetStream streams (running)</h2>
|
||
<p>Created by <code>verae-nats-cluster/scripts/ensure-streams.sh</code>
|
||
(middleware <code>ensureStreams</code> also creates the first
|
||
three).</p>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 15%" />
|
||
<col style="width: 19%" />
|
||
<col style="width: 19%" />
|
||
<col style="width: 17%" />
|
||
<col style="width: 27%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Stream</th>
|
||
<th>Subjects</th>
|
||
<th>Replicas</th>
|
||
<th>Storage</th>
|
||
<th>Who consumes</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>ZAPIER_JOBS</code></td>
|
||
<td><code>verae.zapier.jobs.watch</code></td>
|
||
<td>3</td>
|
||
<td>file</td>
|
||
<td>job-poller (in middleware process), Q <code>job-poller</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>ZAPIER_EVENTS</code></td>
|
||
<td><code>verae.zapier.jobs.events</code></td>
|
||
<td>3</td>
|
||
<td>file</td>
|
||
<td>middleware wait + webhook router
|
||
(<code>event-webhook-router</code>); optional exclusive
|
||
<code>jobs-events</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>ZAPIER_WEBHOOKS</code></td>
|
||
<td><code>verae.zapier.webhooks.deliver</code></td>
|
||
<td>3</td>
|
||
<td>file</td>
|
||
<td>webhook-deliver (in middleware), Q <code>webhook-deliver</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>ZAPIER_USAGE</code></td>
|
||
<td><code>verae.zapier.usage</code></td>
|
||
<td>3</td>
|
||
<td>file</td>
|
||
<td><strong>no durable consumer</strong> (<code>USAGE_WRITER</code>
|
||
named only)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>VERAE_ARCHIVE</code></td>
|
||
<td><code>verae.archive.></code></td>
|
||
<td>3</td>
|
||
<td>file</td>
|
||
<td>worm + tree on LXC 510 (keep); aggregator <strong>not</strong>
|
||
fleet-enabled</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Billing is <strong>core request-reply / pub</strong>, not a JetStream
|
||
stream.</p>
|
||
<h2 id="address-inventory">Address inventory</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 21%" />
|
||
<col style="width: 14%" />
|
||
<col style="width: 24%" />
|
||
<col style="width: 21%" />
|
||
<col style="width: 17%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Address</th>
|
||
<th>Kind</th>
|
||
<th>Designed</th>
|
||
<th>Running</th>
|
||
<th>Notes</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>verae.zapier.jobs.watch</code></td>
|
||
<td>JS work Q <code>job-poller</code></td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>middleware publishes; in-process poller consumes</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.zapier.jobs.events</code></td>
|
||
<td>JS events</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>poller publishes; wait() subscribes; webhook worker routes</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.zapier.webhooks.deliver</code></td>
|
||
<td>JS work Q <code>webhook-deliver</code></td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>same Node process as HTTP</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.zapier.usage</code></td>
|
||
<td>JS optional</td>
|
||
<td>yes</td>
|
||
<td>stream <strong>yes</strong>, consumer <strong>no</strong></td>
|
||
<td>middleware entitlement is <strong>local files</strong></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.internal.jobs.events</code></td>
|
||
<td>core republish</td>
|
||
<td>yes (<code>jobs-events</code> exclusive)</td>
|
||
<td><strong>partial</strong></td>
|
||
<td>only if <code>JOBS_EVENTS_EXCLUSIVE=1</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.billing.statement.get</code></td>
|
||
<td>RR Q <code>account-balance</code></td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>portal, CS, sales, accounting, access-web/api</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.billing.balance.get</code></td>
|
||
<td>RR</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>account-balance</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.billing.balance.adjust</code></td>
|
||
<td>RR</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>CS credits, portal reload</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.billing.usage.recorded</code></td>
|
||
<td>pub</td>
|
||
<td>yes</td>
|
||
<td><strong>yes if edge <code>NATS_URL</code> set</strong></td>
|
||
<td><code>zappier/src/app.ts</code> <code>onUsage</code> →
|
||
<code>natsPublish</code>; account-balance subscribes</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.billing.payment.recorded</code></td>
|
||
<td>pub</td>
|
||
<td>yes</td>
|
||
<td><strong>yes if <code>NATS_URL</code></strong></td>
|
||
<td>portal reload (<code>portal.ts</code>)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.billing.credit.applied</code></td>
|
||
<td>pub</td>
|
||
<td>yes</td>
|
||
<td><strong>yes if <code>NATS_URL</code></strong></td>
|
||
<td>admin/CS (<code>admin.ts</code>)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.billing.customer.put</code></td>
|
||
<td>pub</td>
|
||
<td>yes</td>
|
||
<td><strong>yes if <code>NATS_URL</code></strong></td>
|
||
<td><code>ledger.ts</code>; account-balance applies</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.authz.check</code></td>
|
||
<td>RR Q <code>access-authz</code></td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>fleet keepFloor</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.authz.deny</code></td>
|
||
<td>pub audit</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong> (on deny)</td>
|
||
<td>no SIEM sink</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.web.in</code> /
|
||
<code>verae.access.web.billing.*</code></td>
|
||
<td>ingress</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>mapped after authz,
|
||
e.g. <code>verae.access.web.billing.statement.get</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.api.*</code></td>
|
||
<td>ingress</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.zapier.*</code></td>
|
||
<td>ingress</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>HTTPS plane; Zapier never speaks NATS</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.leaf.in</code> /
|
||
<code>verae.access.leaf.*</code></td>
|
||
<td>ingress</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>no billing</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.staff.*</code></td>
|
||
<td>ingress</td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>CS/sales/accounting</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.identity.bind</code></td>
|
||
<td>RR Q <code>identity</code></td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>fleet</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.identity.lookup</code></td>
|
||
<td>RR Q <code>identity</code></td>
|
||
<td>yes</td>
|
||
<td><strong>yes</strong></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.archive.put</code></td>
|
||
<td>JS (<code>VERAE_ARCHIVE</code>)</td>
|
||
<td>yes</td>
|
||
<td><strong>partial</strong></td>
|
||
<td>splitter <strong>in-process</strong> in middleware; worm/tree on 510
|
||
consume</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.archive.query</code></td>
|
||
<td>broadcast (no Q)</td>
|
||
<td>yes</td>
|
||
<td><strong>partial</strong></td>
|
||
<td>live aggregator hook <strong>TBD</strong> in middleware (simulator
|
||
has it)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.archive.reply.<id></code></td>
|
||
<td>replies on bloom hit</td>
|
||
<td>yes</td>
|
||
<td><strong>partial</strong></td>
|
||
<td>worm/tree on 510 can reply; aggregator not fleet-enabled</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.splitter.in</code></td>
|
||
<td>future</td>
|
||
<td>yes (docs)</td>
|
||
<td><strong>no</strong></td>
|
||
<td>splitter stays a library</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.example.process.in</code> / <code>.out</code> /
|
||
<code>.reply.*</code></td>
|
||
<td>template</td>
|
||
<td>yes</td>
|
||
<td><strong>no</strong></td>
|
||
<td><code>verae-nats-process</code> is a model</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>bench.*</code></td>
|
||
<td>lab only</td>
|
||
<td>n/a</td>
|
||
<td>ephemeral</td>
|
||
<td>speed studies; not product</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Plane mapping (<code>verae-access-authz</code>): internal
|
||
<code>verae.billing.statement.get</code> + plane <code>web</code> →
|
||
ingress <code>verae.access.web.billing.statement.get</code>. Planes
|
||
never publish internal subjects until <code>authz.check</code> returns
|
||
allow.</p>
|
||
<h2 id="durable-consumers-named-in-middleware">Durable consumers (named
|
||
in middleware)</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 18%" />
|
||
<col style="width: 54%" />
|
||
<col style="width: 27%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Stream / subject</th>
|
||
<th>Running</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>job-poller</code></td>
|
||
<td><code>ZAPIER_JOBS</code> / <code>jobs.watch</code></td>
|
||
<td><strong>yes</strong> (in-process)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>event-webhook-router</code></td>
|
||
<td><code>ZAPIER_EVENTS</code> / <code>jobs.events</code></td>
|
||
<td><strong>yes</strong> (in-process)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>webhook-deliver</code></td>
|
||
<td><code>ZAPIER_WEBHOOKS</code> / <code>webhooks.deliver</code></td>
|
||
<td><strong>yes</strong> (in-process)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>usage-writer</code></td>
|
||
<td><code>ZAPIER_USAGE</code> / <code>verae.zapier.usage</code></td>
|
||
<td><strong>no</strong></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>jobs-events-exclusive</code></td>
|
||
<td><code>ZAPIER_EVENTS</code></td>
|
||
<td><strong>no</strong> unless <code>JOBS_EVENTS_EXCLUSIVE=1</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="fleet-overlay-vs-in-process">Fleet overlay vs in-process</h2>
|
||
<p><code>packages/verae-fleet/overlays/ns1/fleet.json</code>
|
||
(2026-09-14):</p>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 23%" />
|
||
<col style="width: 44%" />
|
||
<col style="width: 31%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Service</th>
|
||
<th>fleet <code>enabled</code></th>
|
||
<th>Actual lab</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>middleware-http</td>
|
||
<td>true</td>
|
||
<td><strong>running</strong>; starts job-poller + webhook workers
|
||
in-process when <code>NATS_ENABLED</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>job-poller</td>
|
||
<td><strong>false</strong></td>
|
||
<td>covered by middleware; do not spawn a second copy</td>
|
||
</tr>
|
||
<tr>
|
||
<td>webhook-deliver</td>
|
||
<td><strong>false</strong></td>
|
||
<td>same</td>
|
||
</tr>
|
||
<tr>
|
||
<td>archive-aggregator</td>
|
||
<td><strong>false</strong></td>
|
||
<td><strong>not</strong> running as a fleet worker</td>
|
||
</tr>
|
||
<tr>
|
||
<td>archive-worm / tree-node</td>
|
||
<td><strong>false</strong> on host fleet</td>
|
||
<td><strong>running on LXC 510</strong> via keep
|
||
<code>units.px-worker.json</code> (1 worm + 1 tree)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>account-balance, access-*, identity, jobs-events, CS/sales/acct,
|
||
staff-iam/session, zappier-edge</td>
|
||
<td>true keepFloor</td>
|
||
<td>keepFloor</td>
|
||
</tr>
|
||
<tr>
|
||
<td>zapier-simulator</td>
|
||
<td>true, keepFloor false</td>
|
||
<td>optional</td>
|
||
</tr>
|
||
<tr>
|
||
<td>nats</td>
|
||
<td>managed false</td>
|
||
<td>3-node cluster + leftover host loopback</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h1 id="endpoint-io">Endpoint I/O</h1>
|
||
<p>Each product NATS address: <strong>what it is for</strong>,
|
||
<strong>who sends</strong>, <strong>who receives</strong>, <strong>how
|
||
to attach</strong>.</p>
|
||
<p>Connect with a <strong>long-lived</strong> client
|
||
(reconnect-per-message is the anti-pattern):</p>
|
||
<pre class="text"><code>NATS_URL=nats://10.10.10.21:4222,nats://10.10.10.22:4222,nats://10.10.10.23:4222</code></pre>
|
||
<p>Public edge (token in <code>/etc/nats/public-edge.token</code> on
|
||
NS1): <code>nats://<token>@70.88.205.138:4222</code>. Do not put
|
||
that URL in Zapier or in Verae.</p>
|
||
<hr />
|
||
<h2
|
||
id="verae.zapier.jobs.watch"><code>verae.zapier.jobs.watch</code></h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 50%" />
|
||
<col style="width: 50%" />
|
||
</colgroup>
|
||
<tbody>
|
||
<tr>
|
||
<td>For</td>
|
||
<td>Work queue: “poll Verae until this <code>jobId</code> is
|
||
terminal.”</td>
|
||
</tr>
|
||
<tr>
|
||
<td>In from</td>
|
||
<td>middleware <code>enqueueWatch</code> after
|
||
<code>POST /api/timestamp</code> (or mock) returns
|
||
<code>jobId</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Out to</td>
|
||
<td>job-poller worker (queue <code>job-poller</code>, durable on
|
||
<code>ZAPIER_JOBS</code>)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Then</td>
|
||
<td>poller calls Verae <code>GET /api/status/{jobId}</code> over
|
||
<strong>HTTPS</strong>, not NATS</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Payload: <code>tenantId</code>, <code>veraeUserId</code>,
|
||
<code>jobId</code>, <code>tokenRef</code>, <code>enqueuedAt</code>,
|
||
<code>attempt</code>, <code>maxAttempts</code>, <code>intervalMs</code>,
|
||
<code>traceId</code>. JWT stays in middleware (<code>tokenRef</code>);
|
||
never on Zapier.</p>
|
||
<p><strong>Connect Verae-side:</strong> do <strong>not</strong>
|
||
subscribe this from <code>api.veraetime.net</code>. The poller is the
|
||
Verae HTTP client. To add another watcher, start another
|
||
<code>job-poller</code> consumer in the same queue group.</p>
|
||
<hr />
|
||
<h2
|
||
id="verae.zapier.jobs.events"><code>verae.zapier.jobs.events</code></h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 50%" />
|
||
<col style="width: 50%" />
|
||
</colgroup>
|
||
<tbody>
|
||
<tr>
|
||
<td>For</td>
|
||
<td>Terminal outcomes: <code>timestamp.completed</code> /
|
||
<code>failed</code> / <code>timeout</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>In from</td>
|
||
<td>job-poller after Verae status is not <code>pending</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Out to</td>
|
||
<td>(1) middleware <code>waitForJobEvent</code> (wait HTTP); (2) webhook
|
||
router → <code>webhooks.deliver</code>; (3) optional
|
||
<code>verae-jobs-events</code> mailbox <code>:3030</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>If <code>JOBS_EVENTS_EXCLUSIVE=1</code>, only
|
||
<code>verae-jobs-events</code> is the durable consumer and republishes
|
||
<code>verae.internal.jobs.events</code>. Default lab: middleware wait +
|
||
webhook worker share the stream.</p>
|
||
<p>Payload: <code>event</code>, <code>tenantId</code>,
|
||
<code>jobId</code>, <code>status</code>, <code>traceId</code>,
|
||
<code>emittedAt</code>.</p>
|
||
<hr />
|
||
<h2
|
||
id="verae.zapier.webhooks.deliver"><code>verae.zapier.webhooks.deliver</code></h2>
|
||
<table>
|
||
<tbody>
|
||
<tr>
|
||
<td>For</td>
|
||
<td>HTTPS POST to <code>hooks.zapier.com</code> (REST Hook)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>In from</td>
|
||
<td>webhook worker on completed/failed events</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Out to</td>
|
||
<td>Zapier cloud <strong>HTTPS</strong>, never NATS</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Payload: <code>hookId</code>, <code>tenantId</code>,
|
||
<code>targetUrl</code>, <code>event</code>, <code>payload</code>,
|
||
<code>attempt</code>, <code>traceId</code>. Slow consumer: keep replica
|
||
floors on the worker if you split it out of middleware.</p>
|
||
<hr />
|
||
<h2 id="verae.zapier.usage"><code>verae.zapier.usage</code></h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 50%" />
|
||
<col style="width: 50%" />
|
||
</colgroup>
|
||
<tbody>
|
||
<tr>
|
||
<td>For</td>
|
||
<td>Optional meter of Zap actions on NATS (JetStream)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>In from</td>
|
||
<td>designed: middleware / edge</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Out to</td>
|
||
<td>designed: <code>USAGE_WRITER</code> → books</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Running</td>
|
||
<td><strong>stream exists; no writer; middleware uses local usage
|
||
files</strong></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Prepaid meter that <strong>is</strong> wired uses
|
||
<code>verae.billing.usage.recorded</code> (core pub), not this stream.
|
||
Finish: either publish here from <code>recordUsage</code> or drop the
|
||
stream.</p>
|
||
<hr />
|
||
<h2 id="billing-verae.billing.-account-balance-source-of-truth">Billing
|
||
(<code>verae.billing.*</code>) — account-balance source of truth</h2>
|
||
<p>Request-reply unless noted. Queue <code>account-balance</code>.
|
||
<strong>Not</strong> JetStream.</p>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 29%" />
|
||
<col style="width: 16%" />
|
||
<col style="width: 29%" />
|
||
<col style="width: 25%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Address</th>
|
||
<th>For</th>
|
||
<th>In from</th>
|
||
<th>Out to</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>statement.get</code></td>
|
||
<td>prepaid, credits, usage, payments</td>
|
||
<td>portal, CS, sales, accounting, access-web/api</td>
|
||
<td>reply to requester</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>balance.get</code></td>
|
||
<td>cents only</td>
|
||
<td>internal</td>
|
||
<td>reply</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>balance.adjust</code></td>
|
||
<td>CS credit or portal reload</td>
|
||
<td>CS, access-web (reload kind only)</td>
|
||
<td>books + reply</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>usage.recorded</code></td>
|
||
<td>meter pub</td>
|
||
<td>zappier-edge <code>meter()</code> <code>onUsage</code> when
|
||
<code>NATS_URL</code> set</td>
|
||
<td>account-balance subscriber (debits prepaid)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>payment.recorded</code></td>
|
||
<td>reload/invoice pub</td>
|
||
<td>portal</td>
|
||
<td>books</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>credit.applied</code></td>
|
||
<td>goodwill pub</td>
|
||
<td>CS/admin</td>
|
||
<td>books</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>customer.put</code></td>
|
||
<td>upsert customer</td>
|
||
<td>edge ledger</td>
|
||
<td>books</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Authz <strong>must</strong> pass
|
||
<code>verae.access.authz.check</code> before these from a public plane.
|
||
Leaf plane <strong>cannot</strong> call billing (policy).</p>
|
||
<p>Middleware <code>recordUsage</code> still writes <strong>local tenant
|
||
files</strong> and does <strong>not</strong> publish
|
||
<code>usage.recorded</code>. Two meters until that is unified.</p>
|
||
<hr />
|
||
<h2 id="access-planes">Access planes</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 39%" />
|
||
<col style="width: 21%" />
|
||
<col style="width: 17%" />
|
||
<col style="width: 21%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Address</th>
|
||
<th>For</th>
|
||
<th>In</th>
|
||
<th>Out</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>verae.access.authz.check</code></td>
|
||
<td>allow/deny <code>{ plane, subject, principal }</code></td>
|
||
<td>every plane + edge <code>billing-nats.ts</code></td>
|
||
<td><code>{ allow, reason }</code>; deny also pubs
|
||
<code>authz.deny</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.authz.deny</code></td>
|
||
<td>audit</td>
|
||
<td>authz on deny</td>
|
||
<td><strong>nowhere yet</strong> (no SIEM)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.web.in</code> /
|
||
<code>verae.access.web.billing.*</code></td>
|
||
<td>browser portal</td>
|
||
<td>access-web HTTP</td>
|
||
<td><code>statement.get</code> / <code>balance.adjust</code> kind=reload
|
||
after authz</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.api.*</code></td>
|
||
<td>customer <code>x-api-key</code></td>
|
||
<td>access-api</td>
|
||
<td>statement; HTTPS to middleware for jobs</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.zapier.*</code></td>
|
||
<td>Zapier HTTPS only</td>
|
||
<td>access-zapier</td>
|
||
<td>jobs.watch <strong>via HTTP to middleware</strong>, not Zapier NATS;
|
||
usage.recorded allowed by policy</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.leaf.in</code></td>
|
||
<td>S2S <code>{ token, target, payload }</code></td>
|
||
<td>remote leaf</td>
|
||
<td>archive + jobs after authz; <strong>never billing</strong></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.access.staff.*</code></td>
|
||
<td>CS/sales/accounting</td>
|
||
<td>access-staff</td>
|
||
<td>billing after authz</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Ingress mapping:
|
||
<code>accessAddress(plane, 'verae.billing.statement.get')</code> →
|
||
<code>verae.access.<plane>.billing.statement.get</code>.</p>
|
||
<hr />
|
||
<h2 id="identity">Identity</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 39%" />
|
||
<col style="width: 21%" />
|
||
<col style="width: 17%" />
|
||
<col style="width: 21%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Address</th>
|
||
<th>For</th>
|
||
<th>In</th>
|
||
<th>Out</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>verae.identity.bind</code></td>
|
||
<td>bind email/customerId → <code>veraeUserId</code></td>
|
||
<td>portal / middleware</td>
|
||
<td>reply</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.identity.lookup</code></td>
|
||
<td>lookup by customerId or veraeUserId</td>
|
||
<td>same</td>
|
||
<td>reply</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Queue <code>identity</code>. Fleet keepFloor.</p>
|
||
<hr />
|
||
<h2 id="archive-tree">Archive / tree</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 39%" />
|
||
<col style="width: 21%" />
|
||
<col style="width: 17%" />
|
||
<col style="width: 21%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Address</th>
|
||
<th>For</th>
|
||
<th>In</th>
|
||
<th>Out</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>verae.archive.put</code></td>
|
||
<td>WORM/tree write (<code>kind</code>: publicMeta / privateMeta / file
|
||
/ tree)</td>
|
||
<td>splitter (in-process from middleware today)</td>
|
||
<td>every worm/tree (JS)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.archive.query</code></td>
|
||
<td>broadcast lookup</td>
|
||
<td>aggregator</td>
|
||
<td><strong>all</strong> worm/tree, <strong>no</strong> queue group</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.archive.reply.<correlationId></code></td>
|
||
<td>bloom <strong>hit only</strong></td>
|
||
<td>worm/tree</td>
|
||
<td>aggregator</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Silence = miss. Chain seal does not wait on archive. Bytes never go
|
||
to the chain.</p>
|
||
<hr />
|
||
<h2 id="template-unused">Template / unused</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 28%" />
|
||
<col style="width: 15%" />
|
||
<col style="width: 12%" />
|
||
<col style="width: 15%" />
|
||
<col style="width: 28%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Address</th>
|
||
<th>For</th>
|
||
<th>In</th>
|
||
<th>Out</th>
|
||
<th>Running</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>verae.splitter.in</code></td>
|
||
<td>future split-out splitter process</td>
|
||
<td>middleware</td>
|
||
<td>splitter</td>
|
||
<td><strong>no</strong></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.example.process.in</code></td>
|
||
<td>template worker</td>
|
||
<td>any</td>
|
||
<td><code>example-process</code> Q</td>
|
||
<td><strong>no</strong></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.example.process.out</code></td>
|
||
<td>template fan-out</td>
|
||
<td>template</td>
|
||
<td>peers</td>
|
||
<td><strong>no</strong></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.example.process.reply.<id></code></td>
|
||
<td>template reply</td>
|
||
<td>template</td>
|
||
<td>requester</td>
|
||
<td><strong>no</strong></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.internal.jobs.events</code></td>
|
||
<td>exclusive mailbox republish</td>
|
||
<td>jobs-events</td>
|
||
<td>wait/webhook</td>
|
||
<td><strong>no</strong> unless env</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="how-to-connect-verae-api-to-nats">How to connect <strong>Verae
|
||
API</strong> to NATS</h2>
|
||
<p><code>api.veraetime.net</code> is <strong>HTTPS</strong>. It does not
|
||
subscribe. The glue is middleware:</p>
|
||
<ol type="1">
|
||
<li>Set <code>VERAE_API_BASE_URL=https://api.veraetime.net</code> and
|
||
<code>MOCK_VERAE=false</code>.</li>
|
||
<li>Middleware login (username/password) → JWT stored as
|
||
<code>tokenRef</code>.</li>
|
||
<li><code>POST /api/timestamp</code> → 202 <code>{ jobId }</code>.</li>
|
||
<li>Publish <code>verae.zapier.jobs.watch</code>.</li>
|
||
<li>Poller <code>GET /api/status/{jobId}</code> until
|
||
<code>completed</code> / <code>failed</code>.</li>
|
||
<li>Publish <code>verae.zapier.jobs.events</code>.</li>
|
||
<li>Wait HTTP and/or REST Hook.</li>
|
||
</ol>
|
||
<p>Do <strong>not</strong> point Verae at <code>NATS_URL</code>. Do
|
||
<strong>not</strong> put NATS in Zapier env. A new worker that must see
|
||
seals uses <code>jobs.events</code> or HTTP
|
||
<code>GET /zapier/v1/status/{jobId}</code>.</p>
|
||
<p>Template for a new addressed process: clone
|
||
<code>verae-nats-process</code>, rename
|
||
<code>verae.example.process.*</code>, add the subject to
|
||
<code>verae-access-authz</code> policy for the plane that may call
|
||
it.</p>
|
||
<h1 id="zapier-verae-api-zapier">Zapier → Verae API → Zapier</h1>
|
||
<p>How a timestamp job is submitted, watched, and returned. Lab today:
|
||
<code>MOCK_VERAE=true</code>. Live path is the same HTTP contract with
|
||
<code>MOCK_VERAE=false</code>.</p>
|
||
<h2 id="rule">Rule</h2>
|
||
<pre class="text"><code>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</code></pre>
|
||
<p>Zapier never has <code>NATS_URL</code>. Verae never has
|
||
<code>NATS_URL</code>.</p>
|
||
<h2 id="submit-async-create">1. Submit (async create)</h2>
|
||
<ol type="1">
|
||
<li>Zap action <strong>Create Timestamp</strong> (or curl)
|
||
<code>POST https://zap.zapier.georgelambert.org/zapier/v1/timestamp</code>
|
||
with portal API key <code>zmw_…</code> (not a Verae JWT).</li>
|
||
<li>access-zapier / edge: authz, then <code>meter()</code> records
|
||
cents; if <code>NATS_URL</code> is set,
|
||
<code>natsPublish(verae.billing.usage.recorded)</code>.</li>
|
||
<li>Forwards to middleware <code>POST /zapier/v1/timestamp</code>.</li>
|
||
<li>Middleware <code>checkEntitlement</code> (local plan limits
|
||
today).</li>
|
||
<li><code>veraeClient.createTimestamp</code>:
|
||
<ul>
|
||
<li><strong>Mock:</strong> in-process job, fake <code>jobId</code>.</li>
|
||
<li><strong>Live:</strong>
|
||
<code>POST https://api.veraetime.net/api/timestamp</code> with JWT →
|
||
<strong>202</strong> <code>{ jobId }</code>.</li>
|
||
</ul></li>
|
||
<li>Splitter (in-process): hash vs attachments; optional
|
||
<code>verae.archive.put</code> (bytes never go to chain).</li>
|
||
<li><code>enqueueWatch</code> → JetStream
|
||
<strong><code>verae.zapier.jobs.watch</code></strong>.</li>
|
||
<li>HTTP <strong>202</strong> <code>{ jobId, sha256, existing }</code>
|
||
back to Zapier immediately.</li>
|
||
</ol>
|
||
<p>Same SHA-256 is idempotent (<code>existing: true</code>).</p>
|
||
<h2 id="monitor-job-poller">2. Monitor (job poller)</h2>
|
||
<p>The poller is <strong>in the middleware Node process</strong> when
|
||
<code>NATS_ENABLED=true</code> (<code>startJobPollerWorker</code>).
|
||
Fleet does <strong>not</strong> spawn a second copy
|
||
(<code>job-poller.enabled: false</code>).</p>
|
||
<ol type="1">
|
||
<li>Durable consumer <code>job-poller</code> on <code>ZAPIER_JOBS</code>
|
||
/ <code>verae.zapier.jobs.watch</code>.</li>
|
||
<li>Resolve <code>tokenRef</code> → tenant Verae password →
|
||
<code>POST</code> login → JWT (stays in middleware).</li>
|
||
<li><code>GET /api/status/{jobId}</code> (mock or live timestamp
|
||
server).</li>
|
||
<li><code>pending</code> → <code>nak</code> after
|
||
<code>intervalMs</code> (retry).</li>
|
||
<li><code>completed</code> → publish <code>jobs.events</code>
|
||
<code>{ event: timestamp.completed, status, jobId, tenantId, traceId }</code>.</li>
|
||
<li><code>failed</code> → <code>timestamp.failed</code>. Too many
|
||
attempts → <code>timestamp.timeout</code>.</li>
|
||
<li><code>ack</code> the watch message.</li>
|
||
</ol>
|
||
<p>Live OpenAPI: poll until <code>completed</code> /
|
||
<code>failed</code>. Hash lookup is <strong>501</strong> on live
|
||
(<code>lookupHash</code>). Map <code>certificate</code>,
|
||
<code>timestamp</code>, <code>blockIndex</code>, <code>sha256</code>
|
||
into the receipt Zap samples expect.</p>
|
||
<h2 id="wait-sync-zap-step">3. Wait (sync Zap step)</h2>
|
||
<p><strong>Create Timestamp and Wait</strong> →
|
||
<code>POST /zapier/v1/timestamp/wait</code>.</p>
|
||
<ol type="1">
|
||
<li>Same as submit (watch is already on NATS).</li>
|
||
<li><code>waitForJobEvent(jobId, WAIT_TIMEOUT_MS)</code> subscribes to
|
||
<strong><code>verae.zapier.jobs.events</code></strong>.</li>
|
||
<li>On matching <code>jobId</code> event: return status JSON
|
||
(certificate / receipt).</li>
|
||
<li>On timeout: <code>{ status: pending, jobId }</code> so the REST Hook
|
||
can finish the Zap.</li>
|
||
</ol>
|
||
<p><code>includeAttached</code>: after seal, aggregator should
|
||
<code>verae.archive.query</code>; bloom hits merge extra receipts.
|
||
<strong>Simulator does this; live aggregator hook is TBD.</strong></p>
|
||
<h2 id="push-back-to-zapier">4. Push back to Zapier</h2>
|
||
<ul>
|
||
<li><strong>Wait response:</strong> HTTP body on the same Zap step.</li>
|
||
<li><strong>REST Hook:</strong> Zapier subscribes
|
||
<code>POST /zapier/v1/hooks</code>. On <code>jobs.events</code>,
|
||
middleware enqueues
|
||
<strong><code>verae.zapier.webhooks.deliver</code></strong>. Worker
|
||
POSTs <code>targetUrl</code> (<code>hooks.zapier.com</code>).</li>
|
||
<li><strong>Poll status:</strong> Zap action <strong>Find Job
|
||
Status</strong> → <code>GET /zapier/v1/status/{jobId}</code> →
|
||
middleware <code>GET</code> Verae (or last event).</li>
|
||
</ul>
|
||
<h2 id="verify">5. Verify</h2>
|
||
<p><code>POST /zapier/v1/verify</code> → middleware → Verae
|
||
<code>POST /api/verify</code>. No job watch.</p>
|
||
<h2 id="batch-merkle-designed-same-bus">6. Batch Merkle (designed; same
|
||
bus)</h2>
|
||
<p>From <code>MESSAGE-FLOWS.md</code>:</p>
|
||
<ol type="1">
|
||
<li><code>POST /v1/timestamp/batch</code> — one item per line.</li>
|
||
<li>Middleware hashes leaves, builds Merkle tree.</li>
|
||
<li>Chain seals <strong>only the root</strong>
|
||
(<code>POST /api/timestamp</code> on the root).</li>
|
||
<li>Each leaf proof → <code>verae.archive.put</code>
|
||
<code>kind: tree</code>.</li>
|
||
<li>Central hash lookup of a leaf misses unless
|
||
<code>includeTree=true</code>.</li>
|
||
<li>Aggregator broadcasts <code>verae.archive.query</code>
|
||
<code>kinds=["tree"]</code>.</li>
|
||
<li>Tree node bloom hit → <code>archive.reply.<id></code> with
|
||
proof.</li>
|
||
</ol>
|
||
<p>Live wait path does not yet call the aggregator, so tree-leaf
|
||
receipts are not merged on NS1.</p>
|
||
<h2 id="connect-live-api.veraetime.net">Connect live
|
||
<code>api.veraetime.net</code></h2>
|
||
<p>Still blocked on operator bind credentials. When ready:</p>
|
||
<div class="sourceCode" id="cb5"><pre
|
||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co"># middleware env</span></span>
|
||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="va">MOCK_VERAE</span><span class="op">=</span>false</span>
|
||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="va">VERAE_API_BASE_URL</span><span class="op">=</span>https://api.veraetime.net</span>
|
||
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="va">NATS_ENABLED</span><span class="op">=</span>true</span>
|
||
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="va">NATS_URL</span><span class="op">=</span>nats://10.10.10.21:4222,nats://10.10.10.22:4222,nats://10.10.10.23:4222</span>
|
||
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="va">JETSTREAM_REPLICAS</span><span class="op">=</span>3</span></code></pre></div>
|
||
<p>Then: working Verae login, one live seal, confirm 202/status/verify,
|
||
map receipt fields, REST Hook to a private Zap. See
|
||
<code>zapier-decisions/STATUS.md</code> “Once MOCK_VERAE=false”.</p>
|
||
<h2 id="billing-on-this-path">Billing on this path</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 20%" />
|
||
<col style="width: 41%" />
|
||
<col style="width: 37%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Hop</th>
|
||
<th>Designed</th>
|
||
<th>Running</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Edge meter</td>
|
||
<td><code>verae.billing.usage.recorded</code></td>
|
||
<td><strong>coded</strong>; fires when edge has
|
||
<code>NATS_URL</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Account-balance</td>
|
||
<td>subscribe + debit prepaid; 402 if empty</td>
|
||
<td><strong>subscriber running</strong>; 402 from books not proven on
|
||
every Zap</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Middleware quota</td>
|
||
<td>local files via <code>recordUsage</code></td>
|
||
<td><strong>running</strong>; not NATS</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae.zapier.usage</code></td>
|
||
<td>optional JS meter</td>
|
||
<td>stream only</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="connect-a-new-internal-process">Connect a new internal
|
||
process</h2>
|
||
<div class="sourceCode" id="cb6"><pre
|
||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">NATS_URL</span><span class="op">=</span>nats://10.10.10.21:4222,nats://10.10.10.22:4222,nats://10.10.10.23:4222</span>
|
||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="co"># clone verae-nats-process, rename subjects, register in verae-fleet</span></span></code></pre></div>
|
||
<p>Jobs stay: HTTP in from Zapier → HTTPS to Verae → NATS watch/events →
|
||
HTTP back to Zapier.</p>
|
||
<h1 id="gaps-and-finish-list">Gaps and finish list</h1>
|
||
<p>Modules and addresses that are designed but <strong>not fully
|
||
connected, not running as fleet workers, or not on the live Verae/Zapier
|
||
path</strong>.</p>
|
||
<h2 id="blocked-on-the-operator-not-a-nats-bug">Blocked on the operator
|
||
(not a NATS bug)</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 24%" />
|
||
<col style="width: 32%" />
|
||
<col style="width: 44%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Item</th>
|
||
<th>State</th>
|
||
<th>To finish</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Zapier Platform <code>push</code></td>
|
||
<td>no login</td>
|
||
<td><code>zapier login</code> → register →
|
||
<code>MIDDLEWARE_BASE_URL=https://zap.zapier.georgelambert.org</code> →
|
||
push private app</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Live Verae</td>
|
||
<td><code>MOCK_VERAE=true</code></td>
|
||
<td>credentials; <code>MOCK_VERAE=false</code>; prove one seal</td>
|
||
</tr>
|
||
<tr>
|
||
<td>nkeys / mTLS</td>
|
||
<td>cluster <code>$G</code> only</td>
|
||
<td><code>verae-nats-accounts</code> sketch; flip only after every
|
||
<code>NATS_URL</code> has creds</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Hash lookup on live OpenAPI</td>
|
||
<td>501</td>
|
||
<td>Verae API gap or our store</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="running-on-httpnats-but-incomplete-routing">Running on HTTP/NATS
|
||
but incomplete routing</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 27%" />
|
||
<col style="width: 34%" />
|
||
<col style="width: 20%" />
|
||
<col style="width: 17%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Module</th>
|
||
<th>Running?</th>
|
||
<th>NATS</th>
|
||
<th>Gap</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>middleware HTTP + in-process job-poller + webhook-deliver</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>watch/events/deliver</td>
|
||
<td>live Verae + Zapier push</td>
|
||
</tr>
|
||
<tr>
|
||
<td>account-balance</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>billing RR + <code>usage.recorded</code> sub</td>
|
||
<td>prove 402 from books on a Zap when prepaid is 0</td>
|
||
</tr>
|
||
<tr>
|
||
<td>zappier-edge</td>
|
||
<td><strong>yes</strong></td>
|
||
<td><code>usage.recorded</code> / <code>payment.recorded</code> /
|
||
<code>credit.applied</code> / <code>customer.put</code> <strong>if
|
||
<code>NATS_URL</code></strong></td>
|
||
<td>confirm live process env; Stripe live</td>
|
||
</tr>
|
||
<tr>
|
||
<td>access-* + authz</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>authz + ingress</td>
|
||
<td>—</td>
|
||
</tr>
|
||
<tr>
|
||
<td>identity</td>
|
||
<td><strong>yes</strong> (fleet)</td>
|
||
<td>bind/lookup</td>
|
||
<td>confirm portal always binds <code>veraeUserId</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>jobs-events mailbox <code>:3030</code></td>
|
||
<td><strong>yes</strong> (fleet)</td>
|
||
<td>events counter</td>
|
||
<td>exclusive consumer off unless
|
||
<code>JOBS_EVENTS_EXCLUSIVE=1</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>CS / sales / accounting</td>
|
||
<td><strong>yes</strong></td>
|
||
<td>statement/adjust</td>
|
||
<td>staff UX</td>
|
||
</tr>
|
||
<tr>
|
||
<td>archive-worm + tree-node</td>
|
||
<td><strong>yes on LXC 510</strong> (keep)</td>
|
||
<td><code>archive.put/query/reply</code></td>
|
||
<td>only <strong>1+1</strong> copies; fleet overlay
|
||
<code>enabled: false</code> for extra replicas</td>
|
||
</tr>
|
||
<tr>
|
||
<td>archive-aggregator</td>
|
||
<td><strong>no</strong> as fleet worker</td>
|
||
<td>query/reply</td>
|
||
<td>enable overlay <strong>or</strong> finish in-process hook
|
||
(<code>includeAttached</code> TBD in middleware)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>request-splitter</td>
|
||
<td>library in middleware</td>
|
||
<td><code>archive.put</code></td>
|
||
<td><code>verae.splitter.in</code> not used</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>ZAPIER_USAGE</code> / <code>USAGE_WRITER</code></td>
|
||
<td>stream yes</td>
|
||
<td><code>verae.zapier.usage</code></td>
|
||
<td>no consumer; middleware usage is local files</td>
|
||
</tr>
|
||
<tr>
|
||
<td>fleet job-poller / webhook-deliver <strong>extra</strong>
|
||
copies</td>
|
||
<td>overlay <strong>false</strong></td>
|
||
<td>same subjects</td>
|
||
<td>only enable if you want SSH-spread replicas <strong>instead
|
||
of</strong> in-process (same queue names)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="designed-not-a-live-worker">Designed, not a live worker</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 27%" />
|
||
<col style="width: 31%" />
|
||
<col style="width: 41%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Module</th>
|
||
<th>Address</th>
|
||
<th>What to do</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>verae-nats-process</code></td>
|
||
<td><code>verae.example.process.*</code></td>
|
||
<td>template only; clone to add a function</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-activate</code></td>
|
||
<td>none (HTTPS Zapier)</td>
|
||
<td>optional tiny app; needs Zapier login</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-zapier</code> (full app)</td>
|
||
<td>HTTPS</td>
|
||
<td>push after login</td>
|
||
</tr>
|
||
<tr>
|
||
<td>simulator</td>
|
||
<td>in-process fake NATS</td>
|
||
<td>local <code>:3847</code>; not production</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-nats-accounts</code></td>
|
||
<td>nkeys policy</td>
|
||
<td>not flipped</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="aux-no-product-subjects-or-http-only">Aux (no product subjects,
|
||
or HTTP only)</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 38%" />
|
||
<col style="width: 28%" />
|
||
<col style="width: 33%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Module</th>
|
||
<th>NATS</th>
|
||
<th>Notes</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>verae-keep</code></td>
|
||
<td>none</td>
|
||
<td>HTTP health; restarts units</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-fleet</code></td>
|
||
<td>none</td>
|
||
<td>catalog + keepFloor</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-uptime</code></td>
|
||
<td>none</td>
|
||
<td>off-box HTTPS GET of public doors</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-backup</code></td>
|
||
<td>none</td>
|
||
<td>disk / JetStream snapshot; no live event subject</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>verae-bootstrap</code> / <code>verae-deploy</code> /
|
||
<code>verae-proxmox</code> / <code>verae-ops</code></td>
|
||
<td>none</td>
|
||
<td>install/link</td>
|
||
</tr>
|
||
<tr>
|
||
<td>staff-iam / staff-session / staff-ui</td>
|
||
<td>none</td>
|
||
<td>HTTP cookies</td>
|
||
</tr>
|
||
<tr>
|
||
<td>public NATS edge</td>
|
||
<td>leaf, token</td>
|
||
<td><strong>do not</strong> use from Zapier; rotate token; later nkeys
|
||
LEAF vs INTERNAL</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="billing-triggers-and-event-logging">Billing triggers and event
|
||
logging</h2>
|
||
<table style="width:100%;">
|
||
<colgroup>
|
||
<col style="width: 22%" />
|
||
<col style="width: 28%" />
|
||
<col style="width: 25%" />
|
||
<col style="width: 22%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Signal</th>
|
||
<th>Designed</th>
|
||
<th>Running</th>
|
||
<th>Finish</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Timestamp/verify meter (edge)</td>
|
||
<td><code>verae.billing.usage.recorded</code></td>
|
||
<td><strong>code path exists</strong> (<code>zappier/src/app.ts</code>
|
||
<code>onUsage</code>)</td>
|
||
<td>Confirm <code>NATS_URL</code> on live edge; 402 from account-balance
|
||
when prepaid empty</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Middleware quota</td>
|
||
<td>local files <code>recordUsage</code></td>
|
||
<td><strong>yes</strong></td>
|
||
<td>Either publish <code>usage.recorded</code> too or drop duplicate
|
||
quota</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Optional JS meter</td>
|
||
<td><code>verae.zapier.usage</code> + <code>USAGE_WRITER</code></td>
|
||
<td>stream only</td>
|
||
<td>Wire or delete</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Portal reload</td>
|
||
<td><code>payment.recorded</code> + <code>balance.adjust</code></td>
|
||
<td>coded</td>
|
||
<td>confirm both fire on Stripe/test reload</td>
|
||
</tr>
|
||
<tr>
|
||
<td>CS credit</td>
|
||
<td><code>balance.adjust</code> + <code>credit.applied</code></td>
|
||
<td>coded</td>
|
||
<td>confirm applied pub on live</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Customer upsert</td>
|
||
<td><code>customer.put</code></td>
|
||
<td>coded</td>
|
||
<td>—</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Authz deny audit</td>
|
||
<td><code>verae.access.authz.deny</code></td>
|
||
<td>running on deny</td>
|
||
<td><strong>no SIEM sink</strong></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Job lifecycle log</td>
|
||
<td><code>jobs.events</code></td>
|
||
<td>running</td>
|
||
<td>mailbox counts only — add structured log/export if needed</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Archive put/query</td>
|
||
<td><code>VERAE_ARCHIVE</code></td>
|
||
<td>510 worm/tree</td>
|
||
<td>aggregator not in wait path</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Public edge</td>
|
||
<td>leaf</td>
|
||
<td>running</td>
|
||
<td>rotate token; do not expose <code>:6222</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="ordered-finish-for-message-routing">Ordered finish for message
|
||
routing</h2>
|
||
<ol type="1">
|
||
<li><strong>Do not double-start</strong> job-poller: either in-process
|
||
<strong>or</strong> fleet copies, same queue
|
||
<code>job-poller</code>.</li>
|
||
<li>Confirm edge <strong><code>NATS_URL</code></strong> so
|
||
<code>usage.recorded</code> actually lands; then 402 from books. Unify
|
||
or drop middleware local <code>recordUsage</code>.</li>
|
||
<li>Enable <strong>archive-aggregator</strong> (or in-process
|
||
equivalent) so <code>includeAttached</code> and tree proofs work on
|
||
wait/status.</li>
|
||
<li>Raise worm/tree <strong>replica floors</strong> on 510/more CTs if
|
||
archive HA is required (fleet overlay min 3).</li>
|
||
<li>Operator: Zapier <code>push</code> +
|
||
<code>MOCK_VERAE=false</code>.</li>
|
||
<li>Prove: Zap create → <code>jobs.watch</code> → live
|
||
<code>GET /api/status</code> → <code>jobs.events</code> → wait body +
|
||
REST Hook.</li>
|
||
<li>nkeys/mTLS after every client has creds; then lock down the public
|
||
edge to accounts (LEAF vs INTERNAL).</li>
|
||
<li>Optional: exclusive <code>jobs-events</code> mailbox; SIEM on
|
||
<code>authz.deny</code> and <code>jobs.events</code>;
|
||
<code>verae.splitter.in</code> if splitter is split out;
|
||
<code>USAGE_WRITER</code> or delete <code>ZAPIER_USAGE</code>.</li>
|
||
</ol>
|
||
<h2 id="what-is-already-enough-for-a-mock-zap">What is already enough
|
||
for a mock Zap</h2>
|
||
<p>HTTPS zap/mw → mock create → <code>jobs.watch</code> → in-process
|
||
poller → mock status completed → <code>jobs.events</code> → wait returns
|
||
mock certificate. Archive extras and live chain are the missing
|
||
middle.</p>
|
||
<h1 id="developer-credentials-and-connection-info">Developer credentials
|
||
and connection info</h1>
|
||
<p>Lab pack for the Verae Time × Zapier stack on
|
||
<strong>NS1.GEORGELAMBERT.ORG</strong> (<code>70.88.205.138</code>).
|
||
Values below were checked <strong>2026-09-14</strong>. These are
|
||
<strong>lab defaults</strong>, not production secrets. Rotate the public
|
||
NATS token and admin secrets before treating this as a customer
|
||
environment.</p>
|
||
<p>Zapier cloud <strong>never</strong> gets a NATS URL or a Verae
|
||
password.</p>
|
||
<h2 id="who-you-are-talking-to">1. Who you are talking to</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 50%" />
|
||
<col style="width: 50%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Host</th>
|
||
<th>Role</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>70.88.205.138</code></td>
|
||
<td>NS1 Proxmox. Public HTTPS + public NATS <strong>client</strong>
|
||
edge</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>10.10.10.20</code></td>
|
||
<td>LXC 510 <code>verae-px-worker</code> (worm + tree)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>10.10.10.21–23</code></td>
|
||
<td>LXC 511–513 <code>nats-a/b/c</code> JetStream cluster
|
||
<code>verae</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>SSH to the box: <code>ssh marchon@70.88.205.138</code><br />
|
||
Git SSH:
|
||
<code>ssh://git@git.georgelambert.org:2223/marchon/<repo>.git</code>
|
||
(port <strong>2223</strong>)</p>
|
||
<h2 id="public-https-doors-live">2. Public HTTPS doors (live)</h2>
|
||
<p>Wildcard <code>*.zapier.georgelambert.org</code> →
|
||
<code>70.88.205.138</code>.</p>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 15%" />
|
||
<col style="width: 28%" />
|
||
<col style="width: 18%" />
|
||
<col style="width: 37%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>URL</th>
|
||
<th>Process</th>
|
||
<th>Port</th>
|
||
<th>Last check</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>https://zapier.georgelambert.org/</td>
|
||
<td>docs catalog</td>
|
||
<td>files</td>
|
||
<td>200</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://git.georgelambert.org/</td>
|
||
<td>Forgejo</td>
|
||
<td>:443 / SSH :2223</td>
|
||
<td>200</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://portal.zapier.georgelambert.org/</td>
|
||
<td>access-web → <code>/portal/</code></td>
|
||
<td>:3021</td>
|
||
<td>302 → portal</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://api.zapier.georgelambert.org/health</td>
|
||
<td>access-api</td>
|
||
<td>:3022</td>
|
||
<td>200</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://leaf.zapier.georgelambert.org/health</td>
|
||
<td>access-leaf</td>
|
||
<td>:3023</td>
|
||
<td>200</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://zap.zapier.georgelambert.org/health</td>
|
||
<td>access-zapier</td>
|
||
<td>:3024</td>
|
||
<td>200</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://staff.zapier.georgelambert.org/</td>
|
||
<td>access-staff</td>
|
||
<td>:3025</td>
|
||
<td>302 login</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://login.zapier.georgelambert.org/</td>
|
||
<td>staff-session</td>
|
||
<td>:3027</td>
|
||
<td>200</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://iam.zapier.georgelambert.org/health</td>
|
||
<td>staff-iam</td>
|
||
<td>:3028</td>
|
||
<td>200 (<code>users: 5</code>)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://cs.zapier.georgelambert.org/</td>
|
||
<td>customer-service</td>
|
||
<td>:3011</td>
|
||
<td>302</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://sales.zapier.georgelambert.org/</td>
|
||
<td>sales-pricing</td>
|
||
<td>:3012</td>
|
||
<td>302</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://acct.zapier.georgelambert.org/</td>
|
||
<td>accounting-export</td>
|
||
<td>:3013</td>
|
||
<td>302</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://mw.zapier.georgelambert.org/health</td>
|
||
<td>middleware</td>
|
||
<td>:3100</td>
|
||
<td>200 <code>mockVerae:true</code> <code>natsConnected:true</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://fleet.zapier.georgelambert.org/</td>
|
||
<td>fleet console</td>
|
||
<td>:3850</td>
|
||
<td>302</td>
|
||
</tr>
|
||
<tr>
|
||
<td>https://nats.zapier.georgelambert.org/</td>
|
||
<td>NATS WebSocket (Caddy)</td>
|
||
<td>14422</td>
|
||
<td><strong>400</strong> on GET (expected; it is WS)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Loopback on NS1 (not public): zappier-edge
|
||
<strong><code>127.0.0.1:13000</code></strong> (admin + Stripe + SQLite).
|
||
Tunnel:</p>
|
||
<div class="sourceCode" id="cb7"><pre
|
||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="fu">ssh</span> <span class="at">-L</span> 13000:127.0.0.1:13000 marchon@70.88.205.138</span>
|
||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="co"># then http://127.0.0.1:13000/admin and /docs</span></span></code></pre></div>
|
||
<p>Operator console tunnel:
|
||
<code>ssh -L 3850:127.0.0.1:3850 marchon@70.88.205.138</code></p>
|
||
<h2 id="nats">3. NATS</h2>
|
||
<h3 id="internal-cluster-workers-keep-fleet">Internal cluster (workers,
|
||
keep, fleet)</h3>
|
||
<p>No token. Private <code>vmbr1</code> only.</p>
|
||
<pre class="text"><code>NATS_URL=nats://10.10.10.21:4222,nats://10.10.10.22:4222,nats://10.10.10.23:4222
|
||
JETSTREAM_REPLICAS=3</code></pre>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Port</th>
|
||
<th>Bind</th>
|
||
<th>Use</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>:4222</code></td>
|
||
<td><code>10.10.10.21–23</code></td>
|
||
<td>clients</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>:6222</code></td>
|
||
<td>same, <code>vmbr1</code></td>
|
||
<td>cluster routes — <strong>do not publish</strong></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>:7422</code></td>
|
||
<td>same</td>
|
||
<td>leaf hubs for the public edge — <strong>do not publish</strong></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>:8222</code></td>
|
||
<td><code>127.0.0.1</code> on each LXC</td>
|
||
<td>monitor</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>From a laptop you <strong>cannot</strong> reach
|
||
<code>10.10.10.x</code> unless you are on NS1 or you tunnel:</p>
|
||
<div class="sourceCode" id="cb9"><pre
|
||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="fu">ssh</span> <span class="at">-L</span> 14222:10.10.10.21:4222 <span class="at">-L</span> 14223:10.10.10.22:4222 <span class="at">-L</span> 14224:10.10.10.23:4222 marchon@70.88.205.138</span>
|
||
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">NATS_URL</span><span class="op">=</span>nats://127.0.0.1:14222,nats://127.0.0.1:14223,nats://127.0.0.1:14224</span></code></pre></div>
|
||
<h3 id="public-client-edge-token">Public client edge (token)</h3>
|
||
<p>Live token (file <code>/etc/nats/public-edge.token</code> on
|
||
NS1):</p>
|
||
<pre class="text"><code>4743a49383ce2ce86e99d9d41c45c0004fe75eb23ef3a2b9</code></pre>
|
||
<pre class="text"><code>nats://4743a49383ce2ce86e99d9d41c45c0004fe75eb23ef3a2b9@70.88.205.138:4222
|
||
wss://nats.zapier.georgelambert.org</code></pre>
|
||
<p><code>CONNECT</code> with that token returns <code>PONG</code>
|
||
(checked 2026-09-14). <code>auth_required: true</code>. Server name
|
||
<code>nats-public-edge</code>. <strong>Do not</strong> put this URL in
|
||
Zapier. Recreate:
|
||
<code>bash packages/verae-nats-cluster/scripts/expose-public.sh</code>
|
||
on NS1.</p>
|
||
<p>Leftover lab listeners (ignore for new clients): host
|
||
<code>127.0.0.1:4222</code> docker-proxy, <code>10.10.10.1:4222</code>
|
||
socat. zappier-edge process still has
|
||
<code>NATS_URL=nats://127.0.0.1:4222</code> — billing pubs may miss the
|
||
3-node cluster until that env is flipped.</p>
|
||
<p>nkeys/mTLS: <strong>not</strong> on. Isolation is LAN + token.</p>
|
||
<h2 id="middleware-jobs">4. Middleware (jobs)</h2>
|
||
<p>Live: <code>MOCK_VERAE=true</code>, <code>NATS_ENABLED=true</code>,
|
||
cluster URL as above, <code>ADMIN_SECRET=change-me-admin</code>
|
||
(default), <code>TOKEN_SECRET=dev-secret-change-me</code> (default).</p>
|
||
<h3 id="lab-tenant-created-2026-09-14-for-this-pack">Lab tenant (created
|
||
2026-09-14 for this pack)</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Value</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>tenantId</td>
|
||
<td><code>tenant-dev-lab-local-89ab82de</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>plan</td>
|
||
<td>free</td>
|
||
</tr>
|
||
<tr>
|
||
<td>API key</td>
|
||
<td><code>zmw_pXdwlUUyMHkAvqj3RRoQ2gOG6yAUQlun</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>mock Verae user</td>
|
||
<td><code>lab-dev</code> / <code>lab-dev-pass</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>veraeUserId</td>
|
||
<td><code>vu_20ed6029b99efb2e</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Proven: <code>GET /zapier/v1/auth/me</code> → valid;
|
||
<code>POST /zapier/v1/timestamp</code> → 202 <code>jobId</code>; wait →
|
||
mock certificate <code>blockIndex: 42</code>.</p>
|
||
<div class="sourceCode" id="cb12"><pre
|
||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="va">KEY</span><span class="op">=</span>zmw_pXdwlUUyMHkAvqj3RRoQ2gOG6yAUQlun</span>
|
||
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="ex">curl</span> <span class="at">-sS</span> <span class="at">-H</span> <span class="st">"x-api-key: </span><span class="va">$KEY</span><span class="st">"</span> https://mw.zapier.georgelambert.org/zapier/v1/auth/me</span>
|
||
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="ex">curl</span> <span class="at">-sS</span> <span class="at">-H</span> <span class="st">"x-api-key: </span><span class="va">$KEY</span><span class="st">"</span> <span class="at">-H</span> <span class="st">"content-type: application/json"</span> <span class="dt">\</span></span>
|
||
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a> <span class="at">-d</span> <span class="st">'{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}'</span> <span class="dt">\</span></span>
|
||
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a> https://mw.zapier.georgelambert.org/zapier/v1/timestamp</span>
|
||
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a><span class="ex">curl</span> <span class="at">-sS</span> <span class="at">-H</span> <span class="st">"x-api-key: </span><span class="va">$KEY</span><span class="st">"</span> <span class="at">-H</span> <span class="st">"content-type: application/json"</span> <span class="dt">\</span></span>
|
||
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a> <span class="at">-d</span> <span class="st">'{"sha256":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}'</span> <span class="dt">\</span></span>
|
||
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a> https://mw.zapier.georgelambert.org/zapier/v1/timestamp/wait</span></code></pre></div>
|
||
<p>Customer API plane (forwards <code>x-api-key</code> to
|
||
middleware):</p>
|
||
<div class="sourceCode" id="cb13"><pre
|
||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="ex">curl</span> <span class="at">-sS</span> <span class="at">-H</span> <span class="st">"x-api-key: </span><span class="va">$KEY</span><span class="st">"</span> <span class="at">-H</span> <span class="st">"content-type: application/json"</span> <span class="dt">\</span></span>
|
||
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> <span class="at">-d</span> <span class="st">'{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}'</span> <span class="dt">\</span></span>
|
||
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> https://api.zapier.georgelambert.org/v1/timestamp</span></code></pre></div>
|
||
<p><code>https://zap.zapier.georgelambert.org</code> is the Zapier
|
||
<strong>plane</strong>. It does <strong>not</strong> currently forward
|
||
<code>x-api-key</code> on timestamp (401 if you curl it with a key).
|
||
Zapier Platform push is still waiting on login; when it is live, set
|
||
<code>MIDDLEWARE_BASE_URL=https://zap.zapier.georgelambert.org</code>
|
||
and use a <code>zmw_…</code> key in the Zap.</p>
|
||
<p>Mint another key (mock Verae accepts any username/password):</p>
|
||
<div class="sourceCode" id="cb14"><pre
|
||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="ex">curl</span> <span class="at">-sS</span> <span class="at">-H</span> <span class="st">"content-type: application/json"</span> <span class="dt">\</span></span>
|
||
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a> <span class="at">-d</span> <span class="st">'{"email":"you@lab.local","name":"You","veraeUsername":"you","veraePassword":"you-pass"}'</span> <span class="dt">\</span></span>
|
||
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> https://mw.zapier.georgelambert.org/zapier/v1/signup</span></code></pre></div>
|
||
<p>Admin list/provision:</p>
|
||
<div class="sourceCode" id="cb15"><pre
|
||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="ex">curl</span> <span class="at">-sS</span> <span class="at">-H</span> <span class="st">"x-admin-secret: change-me-admin"</span> <span class="dt">\</span></span>
|
||
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a> https://mw.zapier.georgelambert.org/zapier/v1/admin/tenants</span></code></pre></div>
|
||
<h2 id="portal-zappier-edge-billing-users">5. Portal / zappier-edge
|
||
(billing users)</h2>
|
||
<p>SQLite
|
||
<code>/SSD3/moved/home-marchon/verae-stack/packages/zappier/zappier.db</code>.
|
||
Bind <code>127.0.0.1:13000</code>.</p>
|
||
<p>Seed <strong>customer API keys</strong> (<code>x-api-key</code> on
|
||
edge <code>/v1/*</code>, <strong>not</strong> middleware
|
||
<code>zmw_</code>):</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Customer</th>
|
||
<th>tier</th>
|
||
<th>key</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Ada (free)</td>
|
||
<td>free</td>
|
||
<td><code>key-ada</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Grace (pro)</td>
|
||
<td>pro</td>
|
||
<td><code>key-grace</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Linus (business)</td>
|
||
<td>business</td>
|
||
<td><code>key-linus</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Docs Capture</td>
|
||
<td>free</td>
|
||
<td><code>key-021851970d8a22c6</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>These keys are for the <strong>edge meter / portal</strong>.
|
||
Middleware jobs need a <code>zmw_…</code> key (section 4).</p>
|
||
<p>Seed <strong>admin console</strong> (<code>/admin</code>,
|
||
<code>x-admin-key</code> or login):</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>username</th>
|
||
<th>password</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>admin</code></td>
|
||
<td><code>admin-dev-key</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>demo</code></td>
|
||
<td><code>$$$Adm1n###</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><code>ZAPPIER_ADMIN_KEY</code> default on CS/sales/staff processes:
|
||
<code>admin-dev-key</code>.</p>
|
||
<p>Stripe: <strong>not</strong> live (lab quotes only). Env
|
||
<code>STRIPE_*</code> unset on the sampled edge process.</p>
|
||
<p>Signup more customers at
|
||
https://portal.zapier.georgelambert.org/portal/</p>
|
||
<h2 id="staff-iam">6. Staff IAM</h2>
|
||
<p>https://login.zapier.georgelambert.org/ ·
|
||
https://iam.zapier.georgelambert.org/</p>
|
||
<p>Seed users (defaults; live IAM has these five):</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>username</th>
|
||
<th>password</th>
|
||
<th>role</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>admin</code></td>
|
||
<td><code>admin-dev-key</code></td>
|
||
<td>owner</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>cs</code></td>
|
||
<td><code>cs-dev-key</code></td>
|
||
<td>cs</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>sales</code></td>
|
||
<td><code>sales-dev-key</code></td>
|
||
<td>sales</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>accounting</code></td>
|
||
<td><code>acct-dev-key</code></td>
|
||
<td>accounting</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>operator</code></td>
|
||
<td><code>fleet-dev-key</code></td>
|
||
<td>operator</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><code>STAFF_KEY</code> / session fallback:
|
||
<code>admin-dev-key</code>.</p>
|
||
<h2 id="leaf-plane">7. Leaf plane</h2>
|
||
<p>https://leaf.zapier.georgelambert.org/health →
|
||
<code>verae.access.leaf.in</code></p>
|
||
<p>Live: <code>LEAF_TOKEN=leaf-dev-token</code></p>
|
||
<div class="sourceCode" id="cb16"><pre
|
||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="ex">curl</span> <span class="at">-sS</span> <span class="at">-H</span> <span class="st">"content-type: application/json"</span> <span class="dt">\</span></span>
|
||
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a> <span class="at">-d</span> <span class="st">'{"token":"leaf-dev-token","target":"verae.archive.put","payload":{},"principal":"dev"}'</span> <span class="dt">\</span></span>
|
||
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> https://leaf.zapier.georgelambert.org/</span></code></pre></div>
|
||
<p>Billing targets are <strong>denied</strong> on this plane.</p>
|
||
<h2 id="git-forgejo">8. Git (Forgejo)</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 50%" />
|
||
<col style="width: 50%" />
|
||
</colgroup>
|
||
<tbody>
|
||
<tr>
|
||
<td>Web</td>
|
||
<td>https://git.georgelambert.org/marchon/</td>
|
||
</tr>
|
||
<tr>
|
||
<td>SSH</td>
|
||
<td><code>ssh://git@git.georgelambert.org:2223/marchon/<name>.git</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>User</td>
|
||
<td><code>marchon</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Overview of routes</td>
|
||
<td>https://git.georgelambert.org/marchon/dev-overview</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Monorepo</td>
|
||
<td><code>master-zapier-plan-draft</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Bootstrap</td>
|
||
<td>https://git.georgelambert.org/marchon/verae-bootstrap</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<div class="sourceCode" id="cb17"><pre
|
||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="va">GIT_SSH_COMMAND</span><span class="op">=</span><span class="st">"ssh -p 2223"</span> <span class="fu">git</span> clone ssh://git@git.georgelambert.org:2223/marchon/dev-overview.git</span></code></pre></div>
|
||
<p>Add your SSH public key on Forgejo (user <strong>marchon</strong> or
|
||
a new account). Do not commit deploy tokens.</p>
|
||
<p>Clone-all:</p>
|
||
<div class="sourceCode" id="cb18"><pre
|
||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="fu">git</span> clone ssh://git@git.georgelambert.org:2223/marchon/verae-bootstrap.git</span>
|
||
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="bu">cd</span> verae-bootstrap</span>
|
||
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">VERAE_SRC</span><span class="op">=</span><span class="va">$HOME</span>/verae-src</span>
|
||
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a><span class="fu">bash</span> scripts/fetch.sh ns1-all-in-one</span></code></pre></div>
|
||
<h2 id="environment-cheat-sheet-new-worker-on-the-cluster">9.
|
||
Environment cheat sheet (new worker on the cluster)</h2>
|
||
<div class="sourceCode" id="cb19"><pre
|
||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">NATS_URL</span><span class="op">=</span>nats://10.10.10.21:4222,nats://10.10.10.22:4222,nats://10.10.10.23:4222</span>
|
||
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">JETSTREAM_REPLICAS</span><span class="op">=</span>3</span>
|
||
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">NATS_ENABLED</span><span class="op">=</span>true</span>
|
||
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">MOCK_VERAE</span><span class="op">=</span>true <span class="co"># flip to false only with live Verae login</span></span>
|
||
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">VERAE_API_BASE_URL</span><span class="op">=</span>https://api.veraetime.net</span>
|
||
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">AUTHZ_URL</span><span class="op">=</span>http://127.0.0.1:3020</span>
|
||
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">ZAPPIER_UPSTREAM</span><span class="op">=</span>http://127.0.0.1:3100</span>
|
||
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">ZAPPIER_ADMIN_URL</span><span class="op">=</span>http://127.0.0.1:13000</span>
|
||
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">ZAPPIER_ADMIN_KEY</span><span class="op">=</span>admin-dev-key</span>
|
||
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">LEAF_TOKEN</span><span class="op">=</span>leaf-dev-token</span>
|
||
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">STAFF_IAM_URL</span><span class="op">=</span>http://127.0.0.1:3028</span></code></pre></div>
|
||
<p>Template worker: clone <code>verae-nats-process</code>, rename
|
||
<code>verae.example.process.*</code>.</p>
|
||
<h2 id="not-issued-yet-blocked">10. Not issued yet (blocked)</h2>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 54%" />
|
||
<col style="width: 45%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>Item</th>
|
||
<th>Why</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Zapier Platform deploy key / <code>zapier login</code></td>
|
||
<td>operator has not logged the CLI in</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Live <code>api.veraetime.net</code> username/password</td>
|
||
<td><code>MOCK_VERAE=true</code>; JWT would stay in middleware</td>
|
||
</tr>
|
||
<tr>
|
||
<td>NATS nkeys / account creds</td>
|
||
<td>cluster is <code>$G</code> + LAN</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Stripe live secret</td>
|
||
<td>lab only</td>
|
||
</tr>
|
||
<tr>
|
||
<td>lan-134 SSH worker</td>
|
||
<td>disabled</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>When Zapier login exists:
|
||
<code>MIDDLEWARE_BASE_URL=https://zap.zapier.georgelambert.org</code>,
|
||
auth field <code>api_key</code> = <code>zmw_…</code>.</p>
|
||
<p>When live Verae exists: middleware <code>MOCK_VERAE=false</code> +
|
||
<code>VERAE_API_BASE_URL=https://api.veraetime.net</code> + working
|
||
login. Zapier still never sees that password.</p>
|
||
<h2 id="related-docs">11. Related docs</h2>
|
||
<ul>
|
||
<li>Routes: <a href="01-nats-routes.md">01-nats-routes.md</a></li>
|
||
<li>Endpoint I/O: <a href="02-endpoint-io.md">02-endpoint-io.md</a></li>
|
||
<li>Job path: <a
|
||
href="03-zapier-verae-path.md">03-zapier-verae-path.md</a></li>
|
||
<li>Gaps: <a href="04-gaps-and-finish.md">04-gaps-and-finish.md</a></li>
|
||
<li>Ops start:
|
||
https://zapier.georgelambert.org/packages/verae-ops/GETTING-STARTED.pdf</li>
|
||
<li>Status:
|
||
https://zapier.georgelambert.org/packages/zapier-decisions/STATUS.pdf</li>
|
||
</ul>
|
||
</body>
|
||
</html>
|