<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.completenoobs.com/noobs/index.php?action=history&amp;feed=atom&amp;title=V4call-server-data-flow</id>
	<title>V4call-server-data-flow - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://www.completenoobs.com/noobs/index.php?action=history&amp;feed=atom&amp;title=V4call-server-data-flow"/>
	<link rel="alternate" type="text/html" href="https://www.completenoobs.com/noobs/index.php?title=V4call-server-data-flow&amp;action=history"/>
	<updated>2026-05-26T23:10:45Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.1</generator>
	<entry>
		<id>https://www.completenoobs.com/noobs/index.php?title=V4call-server-data-flow&amp;diff=758&amp;oldid=prev</id>
		<title>AwesomO: Created page with &quot;{{:LICENCE_HEADER_MIT}}   = v4call Server Data Flow — Sign, Announce, Verify, Gate — A Noob Reference =  From CompleteNoobs  A clear-cut reference for which value goes in which field across &lt;code&gt;server-sign.html&lt;/code&gt;, &lt;code&gt;server-announce.html&lt;/code&gt;, the &lt;code&gt;/.well-known/v4call-server.json&lt;/code&gt; file, the Hive announce post, and the four nGate scripts. Use this when you&#039;re not 100% sure what to type into a form, or when nGate-verify gives you an error and you...&quot;</title>
		<link rel="alternate" type="text/html" href="https://www.completenoobs.com/noobs/index.php?title=V4call-server-data-flow&amp;diff=758&amp;oldid=prev"/>
		<updated>2026-05-26T00:36:05Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;{{:LICENCE_HEADER_MIT}}   = v4call Server Data Flow — Sign, Announce, Verify, Gate — A Noob Reference =  From CompleteNoobs  A clear-cut reference for which value goes in which field across &amp;lt;code&amp;gt;server-sign.html&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;server-announce.html&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;/.well-known/v4call-server.json&amp;lt;/code&amp;gt; file, the Hive announce post, and the four nGate scripts. Use this when you&amp;#039;re not 100% sure what to type into a form, or when nGate-verify gives you an error and you...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{:LICENCE_HEADER_MIT}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= v4call Server Data Flow — Sign, Announce, Verify, Gate — A Noob Reference =&lt;br /&gt;
&lt;br /&gt;
From CompleteNoobs&lt;br /&gt;
&lt;br /&gt;
A clear-cut reference for which value goes in which field across &amp;lt;code&amp;gt;server-sign.html&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;server-announce.html&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;/.well-known/v4call-server.json&amp;lt;/code&amp;gt; file, the Hive announce post, and the four nGate scripts. Use this when you&amp;#039;re not 100% sure what to type into a form, or when nGate-verify gives you an error and you don&amp;#039;t know which value caused it.&lt;br /&gt;
&lt;br /&gt;
This doc complements [[Nostr_Relay_With_a_3-Key_Whitelist|stage 1]], [[Nostr_Hands-On|stage 2]], and [[NGate_—_Auto-Whitelist_a_Nostr_Relay|stage 3]] — it&amp;#039;s not a phase, it&amp;#039;s a &amp;quot;wait, I&amp;#039;m confused, what&amp;#039;s what?&amp;quot; reference you can flip to any time.&lt;br /&gt;
&lt;br /&gt;
== The single biggest source of confusion: &amp;#039;&amp;#039;&amp;#039;3 different &amp;quot;accounts&amp;quot;&amp;#039;&amp;#039;&amp;#039; ==&lt;br /&gt;
&lt;br /&gt;
These are three &amp;#039;&amp;#039;&amp;#039;separate&amp;#039;&amp;#039;&amp;#039; things. They might happen to share names. They are not the same.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Thing !! Example !! What it is !! Where it lives&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;HIVE ACCOUNT&amp;#039;&amp;#039;&amp;#039; || &amp;lt;code&amp;gt;cnoobs&amp;lt;/code&amp;gt; || A Hive blockchain account. Has a posting key, an active key, and an owner key. Signs the announce post. || On Hive (chain). Resolved by name lookup.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;ESCROW ACCOUNT&amp;#039;&amp;#039;&amp;#039; || &amp;lt;code&amp;gt;cnoobs-escrow&amp;lt;/code&amp;gt; || ANOTHER Hive blockchain account, used by v4call-server to hold caller funds during paid calls. Its active key must be in &amp;lt;code&amp;gt;V4CALL_ESCROW_KEY&amp;lt;/code&amp;gt; on the v4call server. Default nGate gate target. || On Hive (chain). Different from the HIVE ACCOUNT above. Could be the same account, but usually isn&amp;#039;t (operational separation).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;DOMAIN&amp;#039;&amp;#039;&amp;#039; || &amp;lt;code&amp;gt;call.completenoobs.com&amp;lt;/code&amp;gt; || An &amp;#039;&amp;#039;&amp;#039;internet hostname&amp;#039;&amp;#039;&amp;#039;. Has DNS records. Hosts your v4call server&amp;#039;s HTTPS endpoint AND the &amp;lt;code&amp;gt;.well-known/v4call-server.json&amp;lt;/code&amp;gt; file. || In DNS. Has nothing to do with Hive — domains are not blockchain accounts.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Common confusion patterns&amp;#039;&amp;#039;&amp;#039; from real test sessions:&lt;br /&gt;
* Putting &amp;lt;code&amp;gt;call.completenoobs.com&amp;lt;/code&amp;gt; (a domain) into the HIVE ACCOUNT field. Hive accounts don&amp;#039;t have dots-and-tlds; &amp;lt;code&amp;gt;call.completenoobs.com&amp;lt;/code&amp;gt; is the DOMAIN field.&lt;br /&gt;
* Putting &amp;lt;code&amp;gt;hive-book.com&amp;lt;/code&amp;gt; (the domain) into the HIVE ACCOUNT field, signing it, and ending up with a well-known that says &amp;lt;code&amp;gt;&amp;quot;hive_account&amp;quot;: &amp;quot;hive-book.com&amp;quot;&amp;lt;/code&amp;gt;. nGate-verify catches it and rejects.&lt;br /&gt;
* Staking HP on &amp;lt;code&amp;gt;v4call&amp;lt;/code&amp;gt; (the announcing HIVE ACCOUNT) when the gate is checking &amp;lt;code&amp;gt;v4call-escrow&amp;lt;/code&amp;gt; (the escrow). Staking on the wrong account ≠ staking.&lt;br /&gt;
&lt;br /&gt;
== The data flow (what goes where) ==&lt;br /&gt;
&lt;br /&gt;
 ┌──────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
 │                            YOU (operator)                                │&lt;br /&gt;
 │                Decide your 3 accounts + your domain first                │&lt;br /&gt;
 └────────────────────────────┬─────────────────────────────────────────────┘&lt;br /&gt;
                              │&lt;br /&gt;
                              ▼&lt;br /&gt;
 ┌──────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
 │  Step 1 — server-sign.html                                               │&lt;br /&gt;
 │                                                                          │&lt;br /&gt;
 │  You fill in the IDENTITY card and SERVER CONFIG card and (optionally)   │&lt;br /&gt;
 │  the NOSTR card. Then click &amp;quot;Sign with Hive Keychain&amp;quot;.                   │&lt;br /&gt;
 │                                                                          │&lt;br /&gt;
 │  KEYCHAIN signs a &amp;quot;|&amp;quot;-joined canonical string with the HIVE ACCOUNT&amp;#039;s    │&lt;br /&gt;
 │  posting key. Output is a single signed JSON file.                       │&lt;br /&gt;
 └────────────────────────────┬─────────────────────────────────────────────┘&lt;br /&gt;
                              │&lt;br /&gt;
                              │ Download JSON →&lt;br /&gt;
                              │ deploy to server&amp;#039;s filesystem at&lt;br /&gt;
                              │ /public/.well-known/v4call-server.json&lt;br /&gt;
                              │ Rebuild + restart the v4call container.&lt;br /&gt;
                              ▼&lt;br /&gt;
 ┌──────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
 │  https://YOUR-DOMAIN/.well-known/v4call-server.json                      │&lt;br /&gt;
 │                                                                          │&lt;br /&gt;
 │  Anyone in the world can fetch this. It&amp;#039;s the cryptographic ground       │&lt;br /&gt;
 │  truth: &amp;quot;This Hive account vouches for this domain.&amp;quot;                     │&lt;br /&gt;
 └────────────────────────────┬─────────────────────────────────────────────┘&lt;br /&gt;
                              │&lt;br /&gt;
                              │ &amp;quot;Next step →&amp;quot; link in server-sign.html&lt;br /&gt;
                              │ opens server-announce.html with all the&lt;br /&gt;
                              │ same fields prefilled via querystring&lt;br /&gt;
                              ▼&lt;br /&gt;
 ┌──────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
 │  Step 2 — server-announce.html                                           │&lt;br /&gt;
 │                                                                          │&lt;br /&gt;
 │  Fields are auto-filled from server-sign. Verify they match. Click       │&lt;br /&gt;
 │  &amp;quot;Post to Hive&amp;quot;. KEYCHAIN signs a Hive post broadcast.                   │&lt;br /&gt;
 └────────────────────────────┬─────────────────────────────────────────────┘&lt;br /&gt;
                              │&lt;br /&gt;
                              │ Hive consensus stores the post forever.&lt;br /&gt;
                              │ Tagged &amp;quot;v4call-server&amp;quot;.&lt;br /&gt;
                              ▼&lt;br /&gt;
 ┌──────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
 │  HIVE BLOCKCHAIN — the public directory of v4call servers                │&lt;br /&gt;
 │                                                                          │&lt;br /&gt;
 │  Other nGate operators query this for new candidates.                    │&lt;br /&gt;
 └────────────────────────────┬─────────────────────────────────────────────┘&lt;br /&gt;
                              │&lt;br /&gt;
                              │ ngate-scan.sh queries Hive&lt;br /&gt;
                              ▼&lt;br /&gt;
 ┌──────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
 │  ngate-scan.sh — Phase 3.1                                               │&lt;br /&gt;
 │                                                                          │&lt;br /&gt;
 │  Reads each post&amp;#039;s V4CALL-SERVER-V1 block, emits NDJSON candidate.       │&lt;br /&gt;
 └────────────────────────────┬─────────────────────────────────────────────┘&lt;br /&gt;
                              │&lt;br /&gt;
                              ▼&lt;br /&gt;
 ┌──────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
 │  ngate-verify.sh — Phase 3.2                                             │&lt;br /&gt;
 │                                                                          │&lt;br /&gt;
 │  For each candidate, fetches the well-known JSON and runs FIVE checks:   │&lt;br /&gt;
 │  ─────────────────────────────────────────────────────────────────────   │&lt;br /&gt;
 │                                                                          │&lt;br /&gt;
 │  ① Reachable: HTTPS GET of verify_url returns 200 + parses as JSON       │&lt;br /&gt;
 │  ② Schema: well-known has claim, signature, hive_account, nonce          │&lt;br /&gt;
 │  ③ HIVE-ACCOUNT cross-check:                                             │&lt;br /&gt;
 │     well-known.hive_account == post&amp;#039;s HIVE-ACCOUNT field                 │&lt;br /&gt;
 │  ④ NOSTR-HEX cross-check (defense in depth, optional):                   │&lt;br /&gt;
 │     well-known.nostr_hex == post&amp;#039;s NOSTR-PUBKEY-HEX (if both present)    │&lt;br /&gt;
 │  ⑤ SIGNATURE cross-check:                                                │&lt;br /&gt;
 │     fetches HIVE-ACCOUNT&amp;#039;s posting pubkey from Hive,                     │&lt;br /&gt;
 │     reproduces canonical &amp;quot;|&amp;quot;-joined payload from well-known fields,      │&lt;br /&gt;
 │     verifies signature using @hiveio/dhive                               │&lt;br /&gt;
 └────────────────────────────┬─────────────────────────────────────────────┘&lt;br /&gt;
                              │&lt;br /&gt;
                              ▼&lt;br /&gt;
 ┌──────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
 │  ngate-gate.sh — Phase 3.3                                               │&lt;br /&gt;
 │                                                                          │&lt;br /&gt;
 │  For each verified candidate, picks the gate target:                     │&lt;br /&gt;
 │   • NGATE_GATE_ACCOUNT=escrow (default) → checks ESCROW account          │&lt;br /&gt;
 │   • NGATE_GATE_ACCOUNT=hive_account     → checks HIVE-ACCOUNT             │&lt;br /&gt;
 │                                                                          │&lt;br /&gt;
 │  Fetches HP from Hive, fetches token balance from Hive-Engine,           │&lt;br /&gt;
 │  applies thresholds and OR/AND mode, emits passing candidates.           │&lt;br /&gt;
 └──────────────────────────────────────────────────────────────────────────┘&lt;br /&gt;
&lt;br /&gt;
== server-sign.html — field-by-field reference ==&lt;br /&gt;
&lt;br /&gt;
What to put in each field, what value type, where it ends up. Every field is in BOTH the signed JSON AND (later, by querystring chain) the announce post.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Field !! Type !! Example !! ⚠ Common mistakes&lt;br /&gt;
|-&lt;br /&gt;
| HIVE ACCOUNT (the one that signs) || Hive account name || &amp;lt;code&amp;gt;cnoobs&amp;lt;/code&amp;gt; || ❌ Putting the domain (&amp;lt;code&amp;gt;cnoobs.com&amp;lt;/code&amp;gt;). ❌ Putting an email. ❌ Putting a Nostr npub.&lt;br /&gt;
|-&lt;br /&gt;
| DOMAIN || Hostname (no scheme, no path) || &amp;lt;code&amp;gt;call.completenoobs.com&amp;lt;/code&amp;gt; || ❌ Adding &amp;lt;code&amp;gt;https://&amp;lt;/code&amp;gt; or trailing slash. ❌ Putting a Hive account name.&lt;br /&gt;
|-&lt;br /&gt;
| ESCROW ACCOUNT || Hive account name || &amp;lt;code&amp;gt;cnoobs-escrow&amp;lt;/code&amp;gt; || ❌ Same as HIVE ACCOUNT (allowed but not recommended — operational separation matters). ❌ Putting an account whose active key isn&amp;#039;t on the v4call server.&lt;br /&gt;
|-&lt;br /&gt;
| FEE ACCOUNT || Hive account name || &amp;lt;code&amp;gt;cnoobs&amp;lt;/code&amp;gt; || Often same as HIVE ACCOUNT. ❌ A different chain&amp;#039;s account.&lt;br /&gt;
|-&lt;br /&gt;
| FEDERATION-WS || WebSocket URL || &amp;lt;code&amp;gt;wss://call.completenoobs.com/federation&amp;lt;/code&amp;gt; || ❌ Missing &amp;lt;code&amp;gt;wss://&amp;lt;/code&amp;gt;. ❌ Wrong domain. ❌ Forgetting &amp;lt;code&amp;gt;/federation&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| NOSTR PUBKEY (npub) || Bech32 string starting &amp;lt;code&amp;gt;npub1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;npub1cxgaen…q57qw3d&amp;lt;/code&amp;gt; || ❌ Pasting an nsec (the secret key — never paste secrets into form fields you didn&amp;#039;t write yourself). ❌ Pasting hex into the npub field (use the auto-sync — it&amp;#039;ll auto-fill).&lt;br /&gt;
|-&lt;br /&gt;
| NOSTR PUBKEY (hex) || 64-char lowercase hex || &amp;lt;code&amp;gt;c191dccd…1ba12c&amp;lt;/code&amp;gt; || ❌ Uppercase. ❌ Wrong length. (Auto-fills if you typed a valid npub above.)&lt;br /&gt;
|-&lt;br /&gt;
| NOSTR RELAY 1–5 || WebSocket URLs || &amp;lt;code&amp;gt;wss://nostr.v4call.com&amp;lt;/code&amp;gt; || ❌ Missing &amp;lt;code&amp;gt;wss://&amp;lt;/code&amp;gt;. (Slots 3–5 left blank are silently skipped.)&lt;br /&gt;
|-&lt;br /&gt;
| ISSUED || Auto-filled || timestamp at sign time || (Don&amp;#039;t touch — auto-filled by server-sign.)&lt;br /&gt;
|-&lt;br /&gt;
| NONCE || Auto-filled || random hex || (Don&amp;#039;t touch — auto-filled.)&lt;br /&gt;
|-&lt;br /&gt;
| EXPIRES || Optional date || &amp;lt;code&amp;gt;2027-01-01&amp;lt;/code&amp;gt; || (Most operators leave blank. Set only if you have a specific rotation schedule.)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After signing, you &amp;#039;&amp;#039;&amp;#039;download&amp;#039;&amp;#039;&amp;#039; the signed JSON and &amp;#039;&amp;#039;&amp;#039;deploy&amp;#039;&amp;#039;&amp;#039; it to:&lt;br /&gt;
* The v4call repo&amp;#039;s &amp;lt;code&amp;gt;public/.well-known/v4call-server.json&amp;lt;/code&amp;gt; (overwriting the placeholder).&lt;br /&gt;
* Rebuild + restart: &amp;lt;code&amp;gt;docker compose down &amp;amp;&amp;amp; docker compose build --no-cache &amp;amp;&amp;amp; docker compose up -d&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Test reachable: &amp;lt;code&amp;gt;curl https://YOUR-DOMAIN/.well-known/v4call-server.json&amp;lt;/code&amp;gt; should return your file.&lt;br /&gt;
&lt;br /&gt;
== server-announce.html — field-by-field reference ==&lt;br /&gt;
&lt;br /&gt;
server-announce auto-fills from server-sign&amp;#039;s &amp;quot;Next step →&amp;quot; link. Verify the prefilled fields match what you signed. Don&amp;#039;t post mismatched data.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Field !! Same as server-sign? !! Why it&amp;#039;s here too&lt;br /&gt;
|-&lt;br /&gt;
| HIVE ACCOUNT (poster) || YES — must match || Hive consensus needs to know who&amp;#039;s authoring this post.&lt;br /&gt;
|-&lt;br /&gt;
| DOMAIN || YES || nGate-scan reads this from the post; ngate-verify uses it to fetch the well-known.&lt;br /&gt;
|-&lt;br /&gt;
| ESCROW ACCOUNT || YES || Default gate target. Visible in the directory entry.&lt;br /&gt;
|-&lt;br /&gt;
| FEE ACCOUNT || YES || Visible in the directory entry. Informational.&lt;br /&gt;
|-&lt;br /&gt;
| FEDERATION-WS || YES || How peer servers connect for federation.&lt;br /&gt;
|-&lt;br /&gt;
| VERIFY URL (auto) || Auto-computed from DOMAIN || &amp;lt;code&amp;gt;https://DOMAIN/.well-known/v4call-server.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| SOFTWARE / PROTOCOL || Optional || Forks may put their own software name here.&lt;br /&gt;
|-&lt;br /&gt;
| NOSTR fields || YES (auto-filled from sign) || Optional. Lets nGate-scan pick up the Nostr key claim.&lt;br /&gt;
|-&lt;br /&gt;
| POST TAGS || &amp;lt;code&amp;gt;v4call-server, v4call, hive&amp;lt;/code&amp;gt; || First tag MUST be &amp;lt;code&amp;gt;v4call-server&amp;lt;/code&amp;gt; — that&amp;#039;s what nGate-scan searches for.&lt;br /&gt;
|-&lt;br /&gt;
| INTRO PARAGRAPH || Optional human readable || Just the prose at the top of the post. Doesn&amp;#039;t affect any field parsing.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
After clicking &amp;quot;Post to Hive&amp;quot; + Keychain confirm, the post is live on Hive. &amp;#039;&amp;#039;&amp;#039;The post is permanent&amp;#039;&amp;#039;&amp;#039; — Hive doesn&amp;#039;t allow deletion. If you made a typo, you can publish a NEW post with the corrected fields; nGate-scan picks up the most recent one for each (account, domain) pair.&lt;br /&gt;
&lt;br /&gt;
== What ends up in the signed JSON ==&lt;br /&gt;
&lt;br /&gt;
The JSON file at &amp;lt;code&amp;gt;/.well-known/v4call-server.json&amp;lt;/code&amp;gt;. Public, fetchable from anywhere, signed by your HIVE ACCOUNT&amp;#039;s posting key.&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
   &amp;quot;claim&amp;quot;:         &amp;quot;v4call-server-ownership&amp;quot;,        ← fixed string&lt;br /&gt;
   &amp;quot;domain&amp;quot;:        &amp;quot;call.completenoobs.com&amp;quot;,         ← from DOMAIN&lt;br /&gt;
   &amp;quot;hive_account&amp;quot;:  &amp;quot;cnoobs&amp;quot;,                         ← from HIVE ACCOUNT&lt;br /&gt;
   &amp;quot;escrow&amp;quot;:        &amp;quot;cnoobs-escrow&amp;quot;,                  ← from ESCROW ACCOUNT&lt;br /&gt;
   &amp;quot;fee_account&amp;quot;:   &amp;quot;cnoobs&amp;quot;,                         ← from FEE ACCOUNT&lt;br /&gt;
   &amp;quot;federation_ws&amp;quot;: &amp;quot;wss://call.completenoobs.com/federation&amp;quot;,&lt;br /&gt;
   &amp;quot;issued&amp;quot;:        &amp;quot;2026-05-08T11:18:00Z&amp;quot;,           ← auto&lt;br /&gt;
   &amp;quot;expires&amp;quot;:       &amp;quot;&amp;quot;,                               ← optional&lt;br /&gt;
   &amp;quot;nonce&amp;quot;:         &amp;quot;abc123def456&amp;quot;,                   ← auto&lt;br /&gt;
   &amp;quot;key_type&amp;quot;:      &amp;quot;posting&amp;quot;,                        ← fixed&lt;br /&gt;
   &amp;quot;signature&amp;quot;:     &amp;quot;1f4a3b…&amp;quot;,                        ← Hive ECDSA signature&lt;br /&gt;
&lt;br /&gt;
   // Optional Nostr fields (only present if you filled the NOSTR card):&lt;br /&gt;
   &amp;quot;nostr_npub&amp;quot;:    &amp;quot;npub1cxgaen…q57qw3d&amp;quot;,&lt;br /&gt;
   &amp;quot;nostr_hex&amp;quot;:     &amp;quot;c191dccd…1ba12c&amp;quot;,&lt;br /&gt;
   &amp;quot;nostr_relays&amp;quot;:  [&amp;quot;wss://nostr.v4call.com&amp;quot;, &amp;quot;wss://nostr.hive-book.com&amp;quot;]&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The signature is over a single canonical string built from these fields in a fixed order — server-sign.html computes it; nGate-verify reproduces it. If a single character of any signed field is different between the well-known and what was signed, verification fails.&lt;br /&gt;
&lt;br /&gt;
== What ends up in the Hive post body ==&lt;br /&gt;
&lt;br /&gt;
The announce post body has a markdown-code-block like this (4 leading spaces per line — that&amp;#039;s how Hive renders the block):&lt;br /&gt;
&lt;br /&gt;
     [V4CALL-SERVER-V1]&lt;br /&gt;
     DOMAIN: call.completenoobs.com           ← from DOMAIN field&lt;br /&gt;
     HIVE-ACCOUNT: cnoobs                     ← from HIVE ACCOUNT field&lt;br /&gt;
     ESCROW: cnoobs-escrow                    ← from ESCROW ACCOUNT field&lt;br /&gt;
     FEE-ACCOUNT: cnoobs                      ← from FEE ACCOUNT field&lt;br /&gt;
     FEDERATION-WS: wss://call.completenoobs.com/federation&lt;br /&gt;
     VERIFY-URL: https://call.completenoobs.com/.well-known/v4call-server.json&lt;br /&gt;
     SOFTWARE: v4call&lt;br /&gt;
     PROTOCOL: 0.3&lt;br /&gt;
     NOSTR-PUBKEY: npub1cxgaen…q57qw3d        ← from NOSTR NPUB field&lt;br /&gt;
     NOSTR-PUBKEY-HEX: c191dccd…1ba12c        ← from NOSTR HEX field&lt;br /&gt;
     NOSTR-RELAYS: wss://nostr.v4call.com, wss://nostr.hive-book.com&lt;br /&gt;
     DECLARED: 2026-05-08T11:18:30Z           ← timestamp at post time&lt;br /&gt;
     [/V4CALL-SERVER-V1]&lt;br /&gt;
&lt;br /&gt;
ngate-scan reads exactly this block. Field names are matched case-sensitively after stripping leading whitespace. Lines that don&amp;#039;t match a known field are ignored.&lt;br /&gt;
&lt;br /&gt;
== What nGate-verify cross-checks (and what each rejection means) ==&lt;br /&gt;
&lt;br /&gt;
In order — verify stops at the first failure for each candidate.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Check !! Failure looks like !! Real-world cause !! Fix&lt;br /&gt;
|-&lt;br /&gt;
| Reachable + valid JSON || &amp;lt;code&amp;gt;could not fetch verify_url&amp;lt;/code&amp;gt; || Domain not deployed yet, DNS not propagated, container not rebuilt after dropping in the new well-known file || Deploy the JSON, rebuild + restart container, test with curl&lt;br /&gt;
|-&lt;br /&gt;
| Schema fields exist || &amp;lt;code&amp;gt;well-known missing required fields&amp;lt;/code&amp;gt; || Old or corrupted JSON file || Re-sign via server-sign.html, redeploy&lt;br /&gt;
|-&lt;br /&gt;
| Claim string == &amp;quot;v4call-server-ownership&amp;quot; || &amp;lt;code&amp;gt;wrong claim string&amp;lt;/code&amp;gt; || Someone deployed a non-v4call signed JSON || (Won&amp;#039;t happen if you used server-sign.html)&lt;br /&gt;
|-&lt;br /&gt;
| HIVE ACCOUNT match || &amp;lt;code&amp;gt;well-known hive_account (X) ≠ post&amp;#039;s HIVE-ACCOUNT (Y)&amp;lt;/code&amp;gt; || &amp;#039;&amp;#039;&amp;#039;You typed a different value in the HIVE ACCOUNT field on server-sign vs. on server-announce&amp;#039;&amp;#039;&amp;#039; (or you re-signed and forgot to re-announce) || Re-sign with the correct HIVE ACCOUNT, redeploy, re-announce&lt;br /&gt;
|-&lt;br /&gt;
| Nostr hex match (if both have it) || &amp;lt;code&amp;gt;well-known nostr_hex differs from post&amp;lt;/code&amp;gt; || You signed with one Nostr key, then re-announced with a different one (typo or rotation) || Re-sign with the correct NOSTR HEX, redeploy, re-announce&lt;br /&gt;
|-&lt;br /&gt;
| Expires not in the past || &amp;lt;code&amp;gt;well-known expired at X&amp;lt;/code&amp;gt; || You set an EXPIRES date that has already passed || Re-sign with a fresh ISSUED timestamp (and either no expiry or a future one)&lt;br /&gt;
|-&lt;br /&gt;
| Within-scan hex collision || &amp;lt;code&amp;gt;FORGERY-FLAG hex=… claimed by both @A and @B — both will be REJECTED&amp;lt;/code&amp;gt; || Two operators announcing with the same Nostr hex (one is forged) || Both REJECTED on purpose. Only one operator should hold each Nostr keypair. Re-keying anyone caught in this is mandatory&lt;br /&gt;
|-&lt;br /&gt;
| Signature valid || &amp;lt;code&amp;gt;signature DID NOT verify&amp;lt;/code&amp;gt; || The well-known was tampered with after signing, OR you signed with a different account&amp;#039;s posting key by mistake || Re-sign via server-sign.html (fresh issued + nonce), redeploy&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== What nGate-gate looks at (and which account matters) ==&lt;br /&gt;
&lt;br /&gt;
By default, nGate-gate checks the &amp;#039;&amp;#039;&amp;#039;ESCROW account&amp;#039;&amp;#039;&amp;#039;, not the announcing HIVE ACCOUNT.&lt;br /&gt;
&lt;br /&gt;
;Why escrow?&lt;br /&gt;
:Escrow is the operational account — it actually moves money during paid calls. Gating on its HP/RC enforces &amp;quot;this server can actually operate,&amp;quot; not just &amp;quot;the operator owns a Hive account.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
;When you&amp;#039;d switch to &amp;lt;code&amp;gt;NGATE_GATE_ACCOUNT=hive_account&amp;lt;/code&amp;gt;:&lt;br /&gt;
* Testing with a token gate where the HIVE-ACCOUNT holds the test tokens but the escrow doesn&amp;#039;t (e.g. CNOOBS).&lt;br /&gt;
* Specific governance use cases where the announcer&amp;#039;s reputation matters more than the operational account&amp;#039;s solvency.&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Worked example from a real test session&amp;#039;&amp;#039;&amp;#039; (yours):&lt;br /&gt;
* You staked 3 HP on &amp;lt;code&amp;gt;v4call&amp;lt;/code&amp;gt; (the HIVE-ACCOUNT).&lt;br /&gt;
* nGate-gate&amp;#039;s default = check ESCROW = &amp;lt;code&amp;gt;v4call-escrow&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;v4call-escrow&amp;lt;/code&amp;gt; still has 0 HP.&lt;br /&gt;
* Result: rejected with &amp;lt;code&amp;gt;gate→@v4call-escrow … hp=0.000/3=false&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Fix A (no Hive transactions): &amp;lt;code&amp;gt;NGATE_GATE_ACCOUNT=hive_account NGATE_MIN_HP=3 ./ngate-gate.sh&amp;lt;/code&amp;gt; → checks &amp;lt;code&amp;gt;v4call&amp;lt;/code&amp;gt;&amp;#039;s HP, passes.&lt;br /&gt;
* Fix B (matches default architecture): Power Up 3 HIVE on &amp;lt;code&amp;gt;v4call-escrow&amp;lt;/code&amp;gt; directly.&lt;br /&gt;
&lt;br /&gt;
== Common mistakes — based on real test sessions ==&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Mistake #1: Putting the domain in HIVE ACCOUNT&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Result: well-known has &amp;lt;code&amp;gt;&amp;quot;hive_account&amp;quot;: &amp;quot;your-domain.com&amp;quot;&amp;lt;/code&amp;gt;. nGate-verify rejects with &amp;lt;code&amp;gt;well-known hive_account (your-domain.com) ≠ post&amp;#039;s HIVE-ACCOUNT (your-account)&amp;lt;/code&amp;gt;.&lt;br /&gt;
:Fix: server-sign.html with HIVE ACCOUNT = &amp;lt;code&amp;gt;your-account&amp;lt;/code&amp;gt; (just the Hive name, no .com).&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Mistake #2: Staking HP on the wrong account&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Result: gate fails for an operator you&amp;#039;d expect to pass.&lt;br /&gt;
:Fix: read the &amp;lt;code&amp;gt;(gate→@account)&amp;lt;/code&amp;gt; in the rejection — it tells you which account was checked. Stake there, OR change &amp;lt;code&amp;gt;NGATE_GATE_ACCOUNT&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Mistake #3: Not re-announcing after re-signing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Result: well-known has updated fields, but the post on Hive still has the old ones. nGate-verify cross-checks see a mismatch.&lt;br /&gt;
:Fix: &amp;#039;&amp;#039;&amp;#039;always do server-sign + server-announce together as a pair&amp;#039;&amp;#039;&amp;#039;. The &amp;quot;Next step →&amp;quot; button on server-sign goes to server-announce with prefilled fields exactly to keep these in sync.&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Mistake #4: Pre-Nostr announce posts still in the scan window&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Symptom: &amp;lt;code&amp;gt;skip @user/domain — no nostr_pubkey_hex (pre-Nostr announce; not an error)&amp;lt;/code&amp;gt;.&lt;br /&gt;
:Cause: you announced before nostr fields were added; the old post still appears in Hive&amp;#039;s recent-20 query.&lt;br /&gt;
:Fix: nothing required. Each operator only needs ONE current announce; older posts age out. If you want the relay to stop seeing them sooner, just publish a fresh announce — your latest takes precedence in Hive&amp;#039;s recent-by-created order.&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Mistake #5: Signing without rebuilding the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Result: deployed the new well-known JSON to disk but the v4call Docker container still serves the old one (Docker images bake in static files at build time).&lt;br /&gt;
:Fix: &amp;lt;code&amp;gt;docker compose down &amp;amp;&amp;amp; docker compose build --no-cache &amp;amp;&amp;amp; docker compose up -d&amp;lt;/code&amp;gt; after every well-known change.&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Mistake #6: Posting before deploying the well-known&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Result: announce post is live on Hive; nGate-scan picks it up; nGate-verify tries to fetch the well-known and gets a 404 → rejection.&lt;br /&gt;
:Fix: deploy + rebuild + verify with curl FIRST, then announce. Or fix the well-known and wait for the next scan cycle — eventual consistency.&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Mistake #7: Staking &amp;quot;exactly N HIVE&amp;quot; but the gate at &amp;lt;code&amp;gt;NGATE_MIN_HP=N&amp;lt;/code&amp;gt; still fails&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Symptom: &amp;lt;code&amp;gt;REJECT … gate failed hp=2.999/3=false&amp;lt;/code&amp;gt; (or some near-round-number mismatch).&lt;br /&gt;
:Cause: HIVE → VESTS → HP round-trip uses 6-decimal vesting math. Staking exactly 3 HIVE can land at 2.999something HP, not exactly 3.000. The gate&amp;#039;s comparison is correct (&amp;lt;code&amp;gt;&amp;amp;gt;=&amp;lt;/code&amp;gt; not &amp;lt;code&amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt;), but the input itself is just slightly under the threshold.&lt;br /&gt;
:Fix A: Stake slightly above the threshold (e.g. 4 HIVE for a &amp;lt;code&amp;gt;NGATE_MIN_HP=3&amp;lt;/code&amp;gt; gate).&lt;br /&gt;
:Fix B: Set the threshold slightly below the stake (e.g. &amp;lt;code&amp;gt;NGATE_MIN_HP=2.5&amp;lt;/code&amp;gt;).&lt;br /&gt;
:Both work. The first feels &amp;quot;rounder&amp;quot; to the operator; the second uses fewer HIVE. Either is fine.&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Mistake #8 — a transient Hive RPC outage shows up as an ERROR line&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Symptom: &amp;lt;code&amp;gt;ngate-gate: ERROR … HP fetch failed (transient Hive RPC); not passing&amp;lt;/code&amp;gt; on one or more candidates, with the run summary showing &amp;lt;code&amp;gt;errors=1&amp;lt;/code&amp;gt;+.&lt;br /&gt;
:Cause: Hive node didn&amp;#039;t respond before the script&amp;#039;s timeout. Not your fault; not a config bug.&lt;br /&gt;
:What happens: that candidate is treated as &amp;quot;not passing&amp;quot; for THIS run, so it doesn&amp;#039;t get added/refreshed. The script exits with code 2 (partial fetch errors). Phase 3.4 reads this exit code and refuses to REMOVE any existing entries from the whitelist — so a single bad cycle can&amp;#039;t kick legitimate operators off your relay.&lt;br /&gt;
:Fix: nothing required. Re-run when Hive is healthy. The &amp;quot;ADD-only on partial failure&amp;quot; rule is the design&amp;#039;s safety net; this output is what it looks like working.&lt;br /&gt;
&lt;br /&gt;
== Clean-redo checklist ==&lt;br /&gt;
&lt;br /&gt;
If you&amp;#039;re re-doing an operator&amp;#039;s announce and want to be sure each step is correct, follow this exact order. Tick them off mentally as you go.&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;☐ Confirm your three accounts and one domain&amp;#039;&amp;#039;&amp;#039; on a piece of paper:&lt;br /&gt;
#: HIVE ACCOUNT  = ___________________  (e.g. &amp;lt;code&amp;gt;cnoobs&amp;lt;/code&amp;gt;, no &amp;lt;code&amp;gt;.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
#: ESCROW ACCT   = ___________________  (e.g. &amp;lt;code&amp;gt;cnoobs-escrow&amp;lt;/code&amp;gt;, no &amp;lt;code&amp;gt;.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
#: FEE ACCT      = ___________________  (often same as HIVE ACCOUNT)&lt;br /&gt;
#: DOMAIN        = ___________________  (e.g. &amp;lt;code&amp;gt;call.completenoobs.com&amp;lt;/code&amp;gt;, no scheme/path)&lt;br /&gt;
#: NOSTR NPUB    = ___________________  (from &amp;lt;code&amp;gt;nostr-gen.html&amp;lt;/code&amp;gt;)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;☐ Open server-sign.html&amp;#039;&amp;#039;&amp;#039;. Fill the IDENTITY card, SERVER CONFIG card, and (optionally) NOSTR card with the values above. Verify each line of the form against your paper.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;☐ Click &amp;quot;Sign with Hive Keychain&amp;quot;&amp;#039;&amp;#039;&amp;#039;. Confirm in Keychain.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;☐ Download the signed JSON&amp;#039;&amp;#039;&amp;#039;. Open it in a text editor. Eyeball the top fields — they should match your paper.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;☐ Deploy the JSON to your v4call repo&amp;#039;&amp;#039;&amp;#039; at &amp;lt;code&amp;gt;public/.well-known/v4call-server.json&amp;lt;/code&amp;gt; (overwriting whatever was there).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;☐ Rebuild + restart&amp;#039;&amp;#039;&amp;#039; your v4call container: &amp;lt;code&amp;gt;docker compose down &amp;amp;&amp;amp; docker compose build --no-cache &amp;amp;&amp;amp; docker compose up -d&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;☐ Test reachability&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;curl https://YOUR-DOMAIN/.well-known/v4call-server.json&amp;lt;/code&amp;gt; on your laptop. Should return the same JSON you just deployed.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;☐ Click the &amp;quot;Next step →&amp;quot; link&amp;#039;&amp;#039;&amp;#039; on server-sign.html (it auto-opens server-announce.html with all fields prefilled from the querystring). Verify the prefilled fields match your paper.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;☐ Click &amp;quot;Post to Hive&amp;quot;&amp;#039;&amp;#039;&amp;#039;. Confirm in Keychain. Wait for the success banner.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;☐ Verify end-to-end&amp;#039;&amp;#039;&amp;#039;: from your laptop, run &amp;lt;code&amp;gt;./nGate/scripts/ngate-scan.sh | ./nGate/scripts/ngate-verify.sh&amp;lt;/code&amp;gt;. Look for &amp;lt;code&amp;gt;OK @your-account/your-domain — signature valid&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;☐ Test the gate&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;./ngate-scan.sh | ./ngate-verify.sh | NGATE_MIN_HP=3 ./ngate-gate.sh&amp;lt;/code&amp;gt;. Read the &amp;lt;code&amp;gt;(gate→@account)&amp;lt;/code&amp;gt; on each rejection — that&amp;#039;s the account whose HP it&amp;#039;s checking. Stake there OR pick a different gate target.&lt;br /&gt;
&lt;br /&gt;
If anything fails, the rejection message tells you which check failed — see &amp;quot;What nGate-verify cross-checks&amp;quot; or &amp;quot;What nGate-gate looks at&amp;quot; above for the recipe.&lt;br /&gt;
&lt;br /&gt;
== Quick reference card ==&lt;br /&gt;
&lt;br /&gt;
Print this. Tape it next to your monitor while doing operator work.&lt;br /&gt;
&lt;br /&gt;
 ╔══════════════════════════════════════════════════════════════════════╗&lt;br /&gt;
 ║                  v4call ANNOUNCE QUICK REFERENCE                     ║&lt;br /&gt;
 ╠══════════════════════════════════════════════════════════════════════╣&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║  3 ACCOUNTS + 1 DOMAIN — these are NOT the same thing:               ║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║    HIVE ACCOUNT   →  signs the announce      (e.g. cnoobs)           ║&lt;br /&gt;
 ║    ESCROW ACCT    →  holds funds, gate target (e.g. cnoobs-escrow)   ║&lt;br /&gt;
 ║    FEE ACCT       →  receives platform fees   (often = HIVE ACCT)    ║&lt;br /&gt;
 ║    DOMAIN         →  internet hostname        (call.completenoobs.com)║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║  ALWAYS DO TOGETHER (one breaks if you skip the other):              ║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║    server-sign.html  ─→  deploy + rebuild  ─→  server-announce.html  ║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║  STAKING FOR THE GATE — stake on the right account:                  ║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║    NGATE_GATE_ACCOUNT=escrow (default) → stake on ESCROW             ║&lt;br /&gt;
 ║    NGATE_GATE_ACCOUNT=hive_account     → stake on HIVE ACCT          ║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║  WHEN VERIFY FAILS — read the rejection reason:                      ║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║    &amp;quot;well-known hive_account (X) ≠ post&amp;#039;s HIVE-ACCOUNT (Y)&amp;quot;           ║&lt;br /&gt;
 ║       → typo on one of the two; re-sign with correct value           ║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║    &amp;quot;signature DID NOT verify&amp;quot;                                        ║&lt;br /&gt;
 ║       → well-known tampered with OR signed by wrong account          ║&lt;br /&gt;
 ║       → re-sign cleanly                                              ║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║    &amp;quot;could not fetch verify_url&amp;quot;                                      ║&lt;br /&gt;
 ║       → JSON not deployed OR container not rebuilt                   ║&lt;br /&gt;
 ║       → docker compose down/build/up, curl-test from laptop          ║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║    &amp;quot;skip … no nostr_pubkey_hex&amp;quot;                                      ║&lt;br /&gt;
 ║       → pre-Nostr announce, harmless, ignore                         ║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║  DEFAULT GATE TARGET:                                                ║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ║    Escrow account. (Operational solvency check.)                     ║&lt;br /&gt;
 ║    To check announcing account instead:                              ║&lt;br /&gt;
 ║       NGATE_GATE_ACCOUNT=hive_account                                ║&lt;br /&gt;
 ║                                                                      ║&lt;br /&gt;
 ╚══════════════════════════════════════════════════════════════════════╝&lt;/div&gt;</summary>
		<author><name>AwesomO</name></author>
	</entry>
</feed>