<?xml version="1.0"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>CompleteNoobs  - Recent changes [en]</title>
		<link>https://www.completenoobs.com/noobs/Special:RecentChanges</link>
		<description>Track the most recent changes to the wiki in this feed.</description>
		<language>en</language>
		<generator>MediaWiki 1.43.1</generator>
		<lastBuildDate>Tue, 16 Jun 2026 11:52:50 GMT</lastBuildDate>
		<item>
			<title>User:JosefinaA20</title>
			<link>https://www.completenoobs.com/noobs/User:JosefinaA20</link>
			<guid isPermaLink="false">https://www.completenoobs.com/noobs/User:JosefinaA20</guid>
			<description>&lt;p&gt;User account &lt;a href=&quot;/noobs/index.php?title=User:JosefinaA20&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new mw-userlink&quot; title=&quot;User:JosefinaA20 (page does not exist)&quot;&gt;&lt;bdi&gt;JosefinaA20&lt;/bdi&gt;&lt;/a&gt; was created&lt;/p&gt;
</description>
			<pubDate>Sat, 13 Jun 2026 07:37:15 GMT</pubDate>
			<dc:creator>JosefinaA20</dc:creator>
			<comments>https://www.completenoobs.com/noobs/User_talk:JosefinaA20</comments>
		</item>
		<item>
			<title>Hive Blockchain Create Custom Coins</title>
			<link>https://www.completenoobs.com/noobs/index.php?title=Hive_Blockchain_Create_Custom_Coins&amp;diff=762&amp;oldid=753</link>
			<guid isPermaLink="false">https://www.completenoobs.com/noobs/index.php?title=Hive_Blockchain_Create_Custom_Coins&amp;diff=762&amp;oldid=753</guid>
			<description>&lt;p&gt;&lt;span class=&quot;autocomment&quot;&gt;Exchanging tokens on an exchange&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 17:32, 26 May 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l118&quot;&gt;Line 118:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 118:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* https://hive-engine.com/trade/CNOOBS&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* https://hive-engine.com/trade/CNOOBS&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;code&amp;gt;https://hive-engine.com/trade/&amp;lt;YOUR_TOKEN&amp;gt;&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;code&amp;gt;https://hive-engine.com/trade/&amp;lt;YOUR_TOKEN&amp;gt;&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;== Finding your tokens across the network ==&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;* https://he.dtools.dev/richlist/CNOOBS&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Common Problems and Fixes ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Common Problems and Fixes ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</description>
			<pubDate>Tue, 26 May 2026 17:32:20 GMT</pubDate>
			<dc:creator>AwesomO</dc:creator>
			<comments>https://www.completenoobs.com/noobs/Talk:Hive_Blockchain_Create_Custom_Coins</comments>
		</item>
		<item>
			<title>NGate</title>
			<link>https://www.completenoobs.com/noobs/index.php?title=NGate&amp;diff=761&amp;oldid=756</link>
			<guid isPermaLink="false">https://www.completenoobs.com/noobs/index.php?title=NGate&amp;diff=761&amp;oldid=756</guid>
			<description>&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 00:40, 26 May 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l4&quot;&gt;Line 4:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 4:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Stage 2: https://www.completenoobs.com/noobs/Nostr-handson&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Stage 2: https://www.completenoobs.com/noobs/Nostr-handson&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Data: https://www.completenoobs.com/noobs/V4call-server-data-flow&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;https://www.completenoobs.com/noobs/Ngate_basic&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;https://www.completenoobs.com/noobs/Ngate-strfry-basic&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</description>
			<pubDate>Tue, 26 May 2026 00:40:51 GMT</pubDate>
			<dc:creator>AwesomO</dc:creator>
			<comments>https://www.completenoobs.com/noobs/Talk:NGate</comments>
		</item>
		<item>
			<title>Ngate-strfry-basic</title>
			<link>https://www.completenoobs.com/noobs/index.php?title=Ngate-strfry-basic&amp;diff=760&amp;oldid=0</link>
			<guid isPermaLink="false">https://www.completenoobs.com/noobs/index.php?title=Ngate-strfry-basic&amp;diff=760&amp;oldid=0</guid>
			<description>&lt;p&gt;Created page with &amp;quot;{{:LICENCE_HEADER_MIT}}   = strfry + nGate — A Hot-Reloading Whitelisted Nostr Relay on Ubuntu 24.04 =  From CompleteNoobs  This guide deploys a &amp;#039;&amp;#039;&amp;#039;strfry&amp;#039;&amp;#039;&amp;#039; Nostr relay at &amp;lt;code&amp;gt;nostr.v4call.com&amp;lt;/code&amp;gt; on a fresh Ubuntu 24.04 server, behind TLS, with nGate driving a &amp;#039;&amp;#039;&amp;#039;live, hot-reloading whitelist&amp;#039;&amp;#039;&amp;#039; — the relay&amp;#039;s allowed-publisher list is updated &amp;#039;&amp;#039;&amp;#039;without ever restarting the relay&amp;#039;&amp;#039;&amp;#039;.  This is the &amp;quot;Stage 4&amp;quot; path described in &lt;a href=&quot;/noobs/index.php?title=CLAUDE.md&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;CLAUDE.md (page does not exist)&quot;&gt;CLAUDE.md&lt;/a&gt; / &lt;a href=&quot;/noobs/index.php?title=STATUS.md&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;STATUS.md (page does not exist)&quot;&gt;STATUS.md&lt;/a&gt;. It is...&amp;quot;&lt;/p&gt;
&lt;a href=&quot;https://www.completenoobs.com/noobs/index.php?title=Ngate-strfry-basic&amp;amp;diff=760&quot;&gt;Show changes&lt;/a&gt;</description>
			<pubDate>Tue, 26 May 2026 00:40:18 GMT</pubDate>
			<dc:creator>AwesomO</dc:creator>
			<comments>https://www.completenoobs.com/noobs/Talk:Ngate-strfry-basic</comments>
		</item>
		<item>
			<title>Ngate basic</title>
			<link>https://www.completenoobs.com/noobs/index.php?title=Ngate_basic&amp;diff=759&amp;oldid=0</link>
			<guid isPermaLink="false">https://www.completenoobs.com/noobs/index.php?title=Ngate_basic&amp;diff=759&amp;oldid=0</guid>
			<description>&lt;p&gt;Created page with &amp;quot;{{:LICENCE_HEADER_MIT}}   = nGate — Deploy to a Fresh Ubuntu 24.04 Server =  From CompleteNoobs  This guide walks an operator through deploying nGate on top of an existing &lt;a href=&quot;/noobs/index.php?title=Nostr_Relay_With_a_3-Key_Whitelist&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;Nostr Relay With a 3-Key Whitelist (page does not exist)&quot;&gt;stage-1 nostr-rs-relay&lt;/a&gt; running on a Vultr Ubuntu 24.04 box. After this you&amp;#039;ll have:  * The relay still running as before. * Caddy still terminating TLS as before. * &amp;#039;&amp;#039;&amp;#039;A new sidecar container&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;ngate-sync&amp;lt;/code&amp;gt;) running alongside,   scanning Hive every...&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;
= nGate — Deploy to a Fresh Ubuntu 24.04 Server =&lt;br /&gt;
&lt;br /&gt;
From CompleteNoobs&lt;br /&gt;
&lt;br /&gt;
This guide walks an operator through deploying nGate on top of an existing&lt;br /&gt;
[[Nostr_Relay_With_a_3-Key_Whitelist|stage-1 nostr-rs-relay]] running on a&lt;br /&gt;
Vultr Ubuntu 24.04 box. After this you&amp;#039;ll have:&lt;br /&gt;
&lt;br /&gt;
* The relay still running as before.&lt;br /&gt;
* Caddy still terminating TLS as before.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;A new sidecar container&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;ngate-sync&amp;lt;/code&amp;gt;) running alongside,&lt;br /&gt;
  scanning Hive every 6 hours, updating the relay&amp;#039;s &amp;lt;code&amp;gt;pubkey_whitelist&amp;lt;/code&amp;gt;&lt;br /&gt;
  automatically when v4call-server operators announce themselves.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Cryptographic enforcement&amp;#039;&amp;#039;&amp;#039;: only posts that prove ownership of both&lt;br /&gt;
  the Hive account AND the Nostr key (via mutual attestation) get whitelisted.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;ve already:&lt;br /&gt;
&lt;br /&gt;
* Followed [[Nostr_Relay_With_a_3-Key_Whitelist|stage 1]] and have a working&lt;br /&gt;
  &amp;lt;code&amp;gt;wss://nostr.YOUR-DOMAIN/&amp;lt;/code&amp;gt; relay.&lt;br /&gt;
* Followed [[Nostr_Hands-On|stage 2]] enough to be comfortable with the&lt;br /&gt;
  Nostr protocol fundamentals.&lt;br /&gt;
* Have at least one v4call-server announce post on Hive (signed via&lt;br /&gt;
  &amp;lt;code&amp;gt;server-sign.html&amp;lt;/code&amp;gt;, announced via &amp;lt;code&amp;gt;server-announce.html&amp;lt;/code&amp;gt;)&lt;br /&gt;
  to use as test input.&lt;br /&gt;
&lt;br /&gt;
If you don&amp;#039;t have a v4call-server post yet, you can still deploy nGate and&lt;br /&gt;
test it (the scan will find existing operators&amp;#039; posts), you just won&amp;#039;t be in&lt;br /&gt;
the whitelist until you publish your own.&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
* [[#Step_1:_SSH_to_the_relay_box|1 Step 1: SSH to the relay box]]&lt;br /&gt;
* [[#Step_2:_Install_runtime_dependencies|2 Step 2: Install runtime dependencies]]&lt;br /&gt;
* [[#Step_3:_Clone_nGate|3 Step 3: Clone nGate]]&lt;br /&gt;
* [[#Step_4:_Install_npm_dependencies|4 Step 4: Install npm dependencies]]&lt;br /&gt;
* [[#Step_5:_Bootstrap_config.toml|5 Step 5: Bootstrap config.toml]]&lt;br /&gt;
* [[#Step_6:_Create_seed.toml|6 Step 6: Create seed.toml (CRITICAL — don&amp;#039;t skip)]]&lt;br /&gt;
* [[#Step_7:_Configure_ngate.yaml|7 Step 7: Configure ngate.yaml]]&lt;br /&gt;
* [[#Step_8:_Dry-run_the_chain|8 Step 8: Dry-run the chain]]&lt;br /&gt;
* [[#Step_9:_Merge_sidecar_into_docker-compose.yml|9 Step 9: Merge sidecar into docker-compose.yml]]&lt;br /&gt;
* [[#Step_10:_Build_and_start_the_sidecar|10 Step 10: Build and start the sidecar]]&lt;br /&gt;
* [[#Step_11:_Watch_a_cycle|11 Step 11: Watch a cycle]]&lt;br /&gt;
* [[#Step_12:_Routine_monitoring|12 Step 12: Routine monitoring]]&lt;br /&gt;
* [[#Updating_nGate|13 Updating nGate]]&lt;br /&gt;
* [[#Known_gotchas|14 Known gotchas]]&lt;br /&gt;
* [[#Removing_nGate|15 Removing nGate (back to manual whitelist)]]&lt;br /&gt;
&lt;br /&gt;
== Step 1: SSH to the relay box ==&lt;br /&gt;
&lt;br /&gt;
 ssh root@nostr.YOUR-DOMAIN&lt;br /&gt;
&lt;br /&gt;
If you set up a non-root user during stage 1, use that and prefix everything&lt;br /&gt;
with &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; as needed.&lt;br /&gt;
&lt;br /&gt;
== Step 2: Install runtime dependencies ==&lt;br /&gt;
&lt;br /&gt;
nGate needs a few things beyond what stage 1 installed. Inside the sidecar&lt;br /&gt;
container, all deps are auto-installed (Alpine image bakes Node + jq + curl&lt;br /&gt;
+ docker-cli + yq). Outside the container — for dry-runs and manual sync&lt;br /&gt;
commands — install on the host:&lt;br /&gt;
&lt;br /&gt;
 apt update&lt;br /&gt;
 apt install -y nodejs npm jq curl&lt;br /&gt;
&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
 for c in bash curl jq sed awk node; do command -v $c &amp;gt;/dev/null &amp;amp;&amp;amp; echo &amp;quot;✓ $c&amp;quot; || echo &amp;quot;✗ $c MISSING&amp;quot;; done&lt;br /&gt;
&lt;br /&gt;
All six should print ✓.&lt;br /&gt;
&lt;br /&gt;
== Step 3: Clone nGate ==&lt;br /&gt;
&lt;br /&gt;
The relay lives at &amp;lt;code&amp;gt;/opt/nostr-relay/&amp;lt;/code&amp;gt;. Clone nGate next to it so&lt;br /&gt;
the bind-mount paths in &amp;lt;code&amp;gt;docker-compose.example.yml&amp;lt;/code&amp;gt; work:&lt;br /&gt;
&lt;br /&gt;
 cd /opt/nostr-relay&lt;br /&gt;
 git clone https://github.com/CompleteNoobs/nGate src&lt;br /&gt;
&lt;br /&gt;
That gives you:&lt;br /&gt;
&lt;br /&gt;
 /opt/nostr-relay/&lt;br /&gt;
 ├── config.toml             ← from stage 1&lt;br /&gt;
 ├── Caddyfile               ← from stage 1&lt;br /&gt;
 ├── docker-compose.yml      ← from stage 1&lt;br /&gt;
 ├── data/                   ← from stage 1 (relay sqlite DB)&lt;br /&gt;
 ├── caddy_data/             ← from stage 1&lt;br /&gt;
 └── src/                    ← NEW (nGate repo)&lt;br /&gt;
     ├── scripts/&lt;br /&gt;
     ├── ngate.yaml.example&lt;br /&gt;
     ├── docker-compose.example.yml&lt;br /&gt;
     └── ...&lt;br /&gt;
&lt;br /&gt;
For convenience, symlink the scripts dir + copy the YAML template up to&lt;br /&gt;
&amp;lt;code&amp;gt;/opt/nostr-relay/&amp;lt;/code&amp;gt; (so bind-mount paths are simple):&lt;br /&gt;
&lt;br /&gt;
 cd /opt/nostr-relay&lt;br /&gt;
 ln -s src/scripts scripts&lt;br /&gt;
 cp src/ngate.yaml.example ngate.yaml&lt;br /&gt;
&lt;br /&gt;
Now &amp;lt;code&amp;gt;scripts/ngate-*.sh&amp;lt;/code&amp;gt; work from &amp;lt;code&amp;gt;/opt/nostr-relay/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;💡 Tip&amp;#039;&amp;#039;&amp;#039;: you can also clone nGate anywhere else and adjust the&lt;br /&gt;
&amp;lt;code&amp;gt;NGATE_*&amp;lt;/code&amp;gt; env-vars in &amp;lt;code&amp;gt;ngate.yaml&amp;lt;/code&amp;gt; to point at the&lt;br /&gt;
right paths. The clone-into-&amp;lt;code&amp;gt;src/&amp;lt;/code&amp;gt;-and-symlink approach is just&lt;br /&gt;
the path of least resistance.&lt;br /&gt;
&lt;br /&gt;
== Step 4: Install npm dependencies ==&lt;br /&gt;
&lt;br /&gt;
The Hive ECDSA + Nostr schnorr helpers are Node scripts that need&lt;br /&gt;
&amp;lt;code&amp;gt;@hiveio/dhive&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;nostr-tools&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 cd /opt/nostr-relay/src/scripts&lt;br /&gt;
 npm install&lt;br /&gt;
&lt;br /&gt;
This creates &amp;lt;code&amp;gt;node_modules/&amp;lt;/code&amp;gt; in the scripts folder (~35MB).&lt;br /&gt;
Gitignored — won&amp;#039;t bloat the repo. One-time cost; subsequent&lt;br /&gt;
&amp;lt;code&amp;gt;git pull&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;npm install&amp;lt;/code&amp;gt; is incremental.&lt;br /&gt;
&lt;br /&gt;
Verify both deps are present:&lt;br /&gt;
&lt;br /&gt;
 ls node_modules/@hiveio/dhive/package.json&lt;br /&gt;
 ls node_modules/nostr-tools/package.json&lt;br /&gt;
&lt;br /&gt;
Both should exist.&lt;br /&gt;
&lt;br /&gt;
== Step 5: Bootstrap config.toml ==&lt;br /&gt;
&lt;br /&gt;
Your stage-1 &amp;lt;code&amp;gt;config.toml&amp;lt;/code&amp;gt; has an &amp;lt;code&amp;gt;[authorization]&amp;lt;/code&amp;gt;&lt;br /&gt;
section with hand-pasted hex pubkeys. nGate manages a specific block of&lt;br /&gt;
this file between BEGIN/END marker comments. Run the bootstrap to wrap your&lt;br /&gt;
existing section:&lt;br /&gt;
&lt;br /&gt;
 cd /opt/nostr-relay&lt;br /&gt;
 ./scripts/ngate-apply.sh --bootstrap&lt;br /&gt;
&lt;br /&gt;
Output should look like:&lt;br /&gt;
&lt;br /&gt;
 ngate-apply: ✓ Bootstrapped — wrapped [authorization] in BEGIN/END markers.&lt;br /&gt;
 ngate-apply:   ngate-apply now manages everything between those markers.&lt;br /&gt;
 ngate-apply:   Operator-edited keys go in: /opt/nostr-relay/seed.toml&lt;br /&gt;
&lt;br /&gt;
Inspect &amp;lt;code&amp;gt;config.toml&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 cat /opt/nostr-relay/config.toml&lt;br /&gt;
&lt;br /&gt;
You should see your existing pubkey_whitelist entries, now wrapped:&lt;br /&gt;
&lt;br /&gt;
 # === BEGIN NGATE-MANAGED — DO NOT EDIT BY HAND ===&lt;br /&gt;
 [authorization]&lt;br /&gt;
 pubkey_whitelist = [&lt;br /&gt;
   &amp;quot;your_existing_hex_1&amp;quot;,&lt;br /&gt;
   &amp;quot;your_existing_hex_2&amp;quot;,&lt;br /&gt;
   &amp;quot;your_existing_hex_3&amp;quot;,&lt;br /&gt;
 ]&lt;br /&gt;
 # === END NGATE-MANAGED ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;⚠ From this point forward, don&amp;#039;t hand-edit between those markers.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
nGate will overwrite that region on every sync. Manual additions go in&lt;br /&gt;
&amp;lt;code&amp;gt;seed.toml&amp;lt;/code&amp;gt; (next step).&lt;br /&gt;
&lt;br /&gt;
== Step 6: Create seed.toml (CRITICAL — don&amp;#039;t skip) ==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why this matters&amp;#039;&amp;#039;&amp;#039;: nGate&amp;#039;s first &amp;lt;code&amp;gt;--apply&amp;lt;/code&amp;gt; with&lt;br /&gt;
&amp;lt;code&amp;gt;--allow-removals&amp;lt;/code&amp;gt; can delete entries that aren&amp;#039;t currently&lt;br /&gt;
backed by a valid v4call-server post on Hive — including yours, if your&lt;br /&gt;
operator key isn&amp;#039;t yet announced. Without a seed entry, you can lock&lt;br /&gt;
yourself out.&lt;br /&gt;
&lt;br /&gt;
Make a &amp;lt;code&amp;gt;seed.toml&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;/opt/nostr-relay/seed.toml&amp;lt;/code&amp;gt;&lt;br /&gt;
listing every hex pubkey that should ALWAYS be allowed, regardless of&lt;br /&gt;
discovery state. Most importantly: your own operator key.&lt;br /&gt;
&lt;br /&gt;
 nano /opt/nostr-relay/seed.toml&lt;br /&gt;
&lt;br /&gt;
Paste:&lt;br /&gt;
&lt;br /&gt;
 # nGate seed list — operator-managed, never auto-removed.&lt;br /&gt;
 # One hex pubkey per line. Comments allowed.&lt;br /&gt;
 #&lt;br /&gt;
 # Critical: include your own operator key here so nGate can&amp;#039;t accidentally&lt;br /&gt;
 # lock you out if your v4call-server post temporarily fails verification.&lt;br /&gt;
&lt;br /&gt;
 your_operator_hex_pubkey_here   # @noblemage&amp;#039;s nostr key&lt;br /&gt;
&lt;br /&gt;
If you&amp;#039;ve been running stage 1 with 3 manual keys, you might want all 3 as&lt;br /&gt;
seeds initially. Worst case is one extra entry in the whitelist; not a&lt;br /&gt;
correctness problem.&lt;br /&gt;
&lt;br /&gt;
== Step 7: Configure ngate.yaml ==&lt;br /&gt;
&lt;br /&gt;
Open the YAML config and edit for your relay:&lt;br /&gt;
&lt;br /&gt;
 nano /opt/nostr-relay/ngate.yaml&lt;br /&gt;
&lt;br /&gt;
The defaults are sensible. Key fields to customise:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;instance_name&amp;lt;/code&amp;gt; — appears in logs. e.g. &amp;lt;code&amp;gt;nostr-hive-book&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;scan_interval_seconds&amp;lt;/code&amp;gt; — default 21600 (6 hours). If you&lt;br /&gt;
  want faster updates and don&amp;#039;t mind the Hive RC, drop to 3600 (1 hour).&lt;br /&gt;
* &amp;lt;code&amp;gt;gate.account&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;escrow&amp;lt;/code&amp;gt; (default; gates the&lt;br /&gt;
  operational account that holds caller funds) or &amp;lt;code&amp;gt;hive_account&amp;lt;/code&amp;gt;&lt;br /&gt;
  (gates the announce-signing account).&lt;br /&gt;
* &amp;lt;code&amp;gt;gate.min_hp&amp;lt;/code&amp;gt; — minimum HP staked. &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt; is a fine&lt;br /&gt;
  test threshold. Production servers might want 30+.&lt;br /&gt;
* &amp;lt;code&amp;gt;gate.min_token_*&amp;lt;/code&amp;gt; — leave blank to disable; set to a&lt;br /&gt;
  Hive-Engine token symbol + amount if you want token-gating.&lt;br /&gt;
* &amp;lt;code&amp;gt;max_consecutive_failures&amp;lt;/code&amp;gt; — 3 = ~18 hours of absence before&lt;br /&gt;
  remove. Keep default unless you have a reason.&lt;br /&gt;
* &amp;lt;code&amp;gt;restart_command&amp;lt;/code&amp;gt; — default &amp;lt;code&amp;gt;docker restart nostr-relay&amp;lt;/code&amp;gt;&lt;br /&gt;
  matches the container_name in your docker-compose.yml. Don&amp;#039;t change&lt;br /&gt;
  unless you renamed the relay container.&lt;br /&gt;
* &amp;lt;code&amp;gt;paths.*&amp;lt;/code&amp;gt; — only change if you put nGate somewhere other than&lt;br /&gt;
  &amp;lt;code&amp;gt;/opt/nostr-relay/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Step 8: Dry-run the chain ==&lt;br /&gt;
&lt;br /&gt;
Before turning the sidecar loose, run the full pipeline manually in&lt;br /&gt;
&amp;lt;code&amp;gt;--dry-run&amp;lt;/code&amp;gt; mode (no writes, no restarts):&lt;br /&gt;
&lt;br /&gt;
 cd /opt/nostr-relay&lt;br /&gt;
 ./scripts/ngate-scan.sh 2&amp;gt;/dev/null \&lt;br /&gt;
   | ./scripts/ngate-verify.sh 2&amp;gt;&amp;amp;1 \&lt;br /&gt;
   | NGATE_MIN_HP=3 ./scripts/ngate-gate.sh 2&amp;gt;&amp;amp;1 \&lt;br /&gt;
   | ./scripts/ngate-apply.sh&lt;br /&gt;
&lt;br /&gt;
Expected stderr output sequence:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ngate-scan:&amp;lt;/code&amp;gt; ok, N posts&lt;br /&gt;
* &amp;lt;code&amp;gt;ngate-verify:&amp;lt;/code&amp;gt; OK / REJECT / skip for each candidate&lt;br /&gt;
* &amp;lt;code&amp;gt;ngate-gate:&amp;lt;/code&amp;gt; OK / REJECT for each verified candidate&lt;br /&gt;
* &amp;lt;code&amp;gt;ngate-apply:&amp;lt;/code&amp;gt; seed: X loaded, current: Y in whitelist, stdin:&lt;br /&gt;
  Z passed, summary: add=… keep=… tolerate=… remove=… → DRY RUN, no&lt;br /&gt;
  files changed.&lt;br /&gt;
&lt;br /&gt;
Read the output. Check that:&lt;br /&gt;
&lt;br /&gt;
* Your own operator key is in the SEED count.&lt;br /&gt;
* Your v4call-server post (if you have one) appears as OK in verify and&lt;br /&gt;
  passes the gate (or fails for an understandable reason).&lt;br /&gt;
* The summary &amp;quot;new whitelist size&amp;quot; looks plausible.&lt;br /&gt;
* config.toml hasn&amp;#039;t been modified (&amp;lt;code&amp;gt;cat config.toml&amp;lt;/code&amp;gt; shows the&lt;br /&gt;
  same content as after bootstrap).&lt;br /&gt;
&lt;br /&gt;
If anything looks off, fix it (re-sign + re-announce, adjust ngate.yaml,&lt;br /&gt;
adjust seed.toml) before going live.&lt;br /&gt;
&lt;br /&gt;
== Step 9: Merge sidecar into docker-compose.yml ==&lt;br /&gt;
&lt;br /&gt;
Your stage-1 &amp;lt;code&amp;gt;docker-compose.yml&amp;lt;/code&amp;gt; already has the &amp;lt;code&amp;gt;nostr-relay&amp;lt;/code&amp;gt;&lt;br /&gt;
and &amp;lt;code&amp;gt;caddy&amp;lt;/code&amp;gt; services. The nGate sidecar adds a third. Open both&lt;br /&gt;
files:&lt;br /&gt;
&lt;br /&gt;
 nano /opt/nostr-relay/docker-compose.yml&lt;br /&gt;
 # In another terminal:&lt;br /&gt;
 cat /opt/nostr-relay/src/docker-compose.example.yml&lt;br /&gt;
&lt;br /&gt;
Copy the &amp;lt;code&amp;gt;ngate-sync&amp;lt;/code&amp;gt; service block from the example into your&lt;br /&gt;
real &amp;lt;code&amp;gt;docker-compose.yml&amp;lt;/code&amp;gt;. The block looks like:&lt;br /&gt;
&lt;br /&gt;
 services:&lt;br /&gt;
   # ... your existing nostr-relay + caddy services ...&lt;br /&gt;
&lt;br /&gt;
   ngate-sync:&lt;br /&gt;
     build:&lt;br /&gt;
       context: .&lt;br /&gt;
       dockerfile: src/scripts/Dockerfile&lt;br /&gt;
     container_name: ngate-sync&lt;br /&gt;
     restart: unless-stopped&lt;br /&gt;
     volumes:&lt;br /&gt;
       - ./src/scripts:/app/scripts&lt;br /&gt;
       - ./ngate.yaml:/app/ngate.yaml:ro&lt;br /&gt;
       - ./seed.toml:/app/seed.toml:ro&lt;br /&gt;
       - ./config.toml:/app/config.toml&lt;br /&gt;
       - ./state.json:/app/state.json&lt;br /&gt;
       - /var/run/docker.sock:/var/run/docker.sock&lt;br /&gt;
     environment:&lt;br /&gt;
       - NGATE_YAML=/app/ngate.yaml&lt;br /&gt;
     depends_on:&lt;br /&gt;
       - nostr-relay&lt;br /&gt;
     networks:&lt;br /&gt;
       - relaynet&lt;br /&gt;
&lt;br /&gt;
(Note: &amp;lt;code&amp;gt;./src/scripts&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;./scripts&amp;lt;/code&amp;gt; because&lt;br /&gt;
we kept the cloned repo at &amp;lt;code&amp;gt;src/&amp;lt;/code&amp;gt; and symlinked. If you did it&lt;br /&gt;
differently, adjust the volume paths.)&lt;br /&gt;
&lt;br /&gt;
Make sure to pre-create the state.json so the bind-mount works:&lt;br /&gt;
&lt;br /&gt;
 echo &amp;#039;{&amp;quot;last_run&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;last_apply&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;restart_log&amp;quot;:[],&amp;quot;candidates&amp;quot;:{}}&amp;#039; &amp;gt; /opt/nostr-relay/state.json&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;⚠ Security note&amp;#039;&amp;#039;&amp;#039;: the sidecar mounts &amp;lt;code&amp;gt;/var/run/docker.sock&amp;lt;/code&amp;gt;&lt;br /&gt;
so it can &amp;lt;code&amp;gt;docker restart nostr-relay&amp;lt;/code&amp;gt;. This gives the sidecar&lt;br /&gt;
container effective root on the host. Acceptable on a single-purpose relay&lt;br /&gt;
box. NOT acceptable on a multi-tenant machine. If that bothers you, set&lt;br /&gt;
&amp;lt;code&amp;gt;restart_command&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;ngate.yaml&amp;lt;/code&amp;gt; to a noop (e.g.&lt;br /&gt;
&amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;) and restart the relay manually after each cycle —&lt;br /&gt;
configuration writes still work; only the restart is skipped.&lt;br /&gt;
&lt;br /&gt;
== Step 10: Build and start the sidecar ==&lt;br /&gt;
&lt;br /&gt;
 cd /opt/nostr-relay&lt;br /&gt;
 docker compose build ngate-sync&lt;br /&gt;
 docker compose up -d ngate-sync&lt;br /&gt;
&lt;br /&gt;
First build downloads node:20-alpine + yq + docker-cli (~150MB). Subsequent&lt;br /&gt;
builds are fast (cached layers). On startup the sidecar runs&lt;br /&gt;
&amp;lt;code&amp;gt;npm install&amp;lt;/code&amp;gt; inside the bind-mounted scripts dir if&lt;br /&gt;
&amp;lt;code&amp;gt;node_modules&amp;lt;/code&amp;gt; isn&amp;#039;t present — should be quick if you already&lt;br /&gt;
ran step 4.&lt;br /&gt;
&lt;br /&gt;
Check the container&amp;#039;s running:&lt;br /&gt;
&lt;br /&gt;
 docker compose ps ngate-sync&lt;br /&gt;
&lt;br /&gt;
Should show &amp;lt;code&amp;gt;Up&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Step 11: Watch a cycle ==&lt;br /&gt;
&lt;br /&gt;
Tail the logs:&lt;br /&gt;
&lt;br /&gt;
 docker compose logs -f ngate-sync&lt;br /&gt;
&lt;br /&gt;
The sidecar runs ONE cycle immediately at startup, then sleeps for&lt;br /&gt;
&amp;lt;code&amp;gt;scan_interval_seconds&amp;lt;/code&amp;gt; between cycles. The first cycle should&lt;br /&gt;
show:&lt;br /&gt;
&lt;br /&gt;
 [2026-05-13T…] [nostr-hive-book] starting ngate-sync instance=… interval=21600s …&lt;br /&gt;
 [2026-05-13T…] [nostr-hive-book] ─── cycle starting ───&lt;br /&gt;
 ngate-scan: trying https://api.hive.blog …&lt;br /&gt;
 ngate-scan:   ok, N posts&lt;br /&gt;
 ngate-scan: scan complete&lt;br /&gt;
 ngate-verify: OK @cnoobs/call.completenoobs.com — Hive sig + Nostr attestation valid …&lt;br /&gt;
 ngate-verify: OK @v4call/v4call.com — Hive sig + Nostr attestation valid …&lt;br /&gt;
 ngate-verify: OK @hive-book/hive-book.com — Hive sig + Nostr attestation valid …&lt;br /&gt;
 ngate-gate: config: account=escrow mode=or hp_enabled=1 (&amp;gt;=3, delegated=false) …&lt;br /&gt;
 ngate-gate: OK @cnoobs/… — passed via hp hp=…&lt;br /&gt;
 ngate-gate: complete — total=3 passed=3 rejected=0 errors=0&lt;br /&gt;
 ngate-apply: seed: 1 pubkey(s) loaded from /app/seed.toml&lt;br /&gt;
 ngate-apply: current: 3 pubkey(s) in /app/config.toml whitelist&lt;br /&gt;
 ngate-apply: stdin: 3 candidate(s) passed gate this run&lt;br /&gt;
 ngate-apply: summary: add=… keep=… tolerate=0 remove=0 seed=1 → new whitelist size = …&lt;br /&gt;
 ngate-apply: ✓ wrote new whitelist to /app/config.toml&lt;br /&gt;
 ngate-apply: restarting relay: docker restart nostr-relay&lt;br /&gt;
 ngate-apply: ✓ restart succeeded&lt;br /&gt;
 [2026-05-13T…] [nostr-hive-book] ─── cycle complete (apply ok) ───&lt;br /&gt;
 [2026-05-13T…] [nostr-hive-book] sleeping 21600s until next cycle&lt;br /&gt;
&lt;br /&gt;
After this, your &amp;lt;code&amp;gt;config.toml&amp;lt;/code&amp;gt;&amp;#039;s nGate-managed block should&lt;br /&gt;
reflect the discovered+gated set.&lt;br /&gt;
&lt;br /&gt;
Verify the relay still works:&lt;br /&gt;
&lt;br /&gt;
 docker compose ps nostr-relay   # Up&lt;br /&gt;
 # From your laptop:&lt;br /&gt;
 nak req -k 1 -a YOUR_HEX wss://nostr.YOUR-DOMAIN&lt;br /&gt;
&lt;br /&gt;
If it returns events, the restart worked cleanly.&lt;br /&gt;
&lt;br /&gt;
== Step 12: Routine monitoring ==&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Status snapshot&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:From inside the sidecar:&lt;br /&gt;
: &amp;lt;code&amp;gt;docker compose exec ngate-sync /app/scripts/ngate-status.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
:Or from the host (env-vars on the command):&lt;br /&gt;
:&lt;br /&gt;
:  NGATE_YAML=/opt/nostr-relay/ngate.yaml \&lt;br /&gt;
:  NGATE_STATE_PATH=/opt/nostr-relay/state.json \&lt;br /&gt;
:  NGATE_CONFIG_PATH=/opt/nostr-relay/config.toml \&lt;br /&gt;
:  NGATE_SEED_PATH=/opt/nostr-relay/seed.toml \&lt;br /&gt;
:  /opt/nostr-relay/scripts/ngate-status.sh&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Logs&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:* &amp;lt;code&amp;gt;docker compose logs -f ngate-sync&amp;lt;/code&amp;gt; — live stream&lt;br /&gt;
:* &amp;lt;code&amp;gt;tail -F /opt/nostr-relay/ngate-sync.log&amp;lt;/code&amp;gt; — bind-mounted host file&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Manual sync&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:If you want a fresh cycle right now (don&amp;#039;t wait 6 hours):&lt;br /&gt;
: &amp;lt;code&amp;gt;docker compose exec ngate-sync /app/scripts/ngate-sync.sh --once&amp;lt;/code&amp;gt;&lt;br /&gt;
:Or restart the sidecar (runs one cycle on startup):&lt;br /&gt;
: &amp;lt;code&amp;gt;docker compose restart ngate-sync&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Inspect state.json&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Shows per-key first-seen, last-seen, consecutive_misses, source&lt;br /&gt;
:(discovery|seed|removed):&lt;br /&gt;
: &amp;lt;code&amp;gt;jq . /opt/nostr-relay/state.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Updating nGate ==&lt;br /&gt;
&lt;br /&gt;
When the repo updates:&lt;br /&gt;
&lt;br /&gt;
 cd /opt/nostr-relay/src&lt;br /&gt;
 git pull&lt;br /&gt;
 cd scripts &amp;amp;&amp;amp; npm install   # in case dep versions changed&lt;br /&gt;
 cd /opt/nostr-relay&lt;br /&gt;
 docker compose build ngate-sync   # only needed if Dockerfile changed&lt;br /&gt;
 docker compose restart ngate-sync&lt;br /&gt;
&lt;br /&gt;
The scripts dir is bind-mounted, so script-only changes take effect on next&lt;br /&gt;
sync without rebuild. Restart picks them up immediately.&lt;br /&gt;
&lt;br /&gt;
== Known gotchas ==&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;&amp;quot;Keychain not detected&amp;quot; in server-announce&amp;#039;&amp;#039;&amp;#039; (browser-side, not nGate)&lt;br /&gt;
:Some browsers don&amp;#039;t expose &amp;lt;code&amp;gt;window.hive_keychain&amp;lt;/code&amp;gt; even with the&lt;br /&gt;
:extension installed (iOS Safari, Brave on iOS, etc.). server-announce.html&lt;br /&gt;
:has a posting-key paste fallback that uses dhive to broadcast directly —&lt;br /&gt;
:scroll past the Keychain button to the &amp;quot;POSTING KEY PASTE&amp;quot; section. Key&lt;br /&gt;
:never leaves your browser.&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;nGate-verify rejects with &amp;quot;no nostr_attestation in well-known OR post&amp;quot;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Your v4call-server post is pre-attestation, OR the well-known doesn&amp;#039;t have&lt;br /&gt;
:the &amp;lt;code&amp;gt;nostr_attestation&amp;lt;/code&amp;gt; field, OR the post body doesn&amp;#039;t have a&lt;br /&gt;
:&amp;lt;code&amp;gt;NOSTR-ATTESTATION:&amp;lt;/code&amp;gt; base64 line. Re-sign in&lt;br /&gt;
:&amp;lt;code&amp;gt;server-sign.html&amp;lt;/code&amp;gt; (Option B canonical) and re-announce. The&lt;br /&gt;
:full chain produces both the well-known field AND the post-body line in&lt;br /&gt;
:one signed event.&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;nGate-verify rejects with &amp;quot;Hive signature DID NOT verify&amp;quot;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Most common cause: signature was computed under a DIFFERENT canonical&lt;br /&gt;
:payload shape than what nGate-verify reconstructs. Re-sign with the&lt;br /&gt;
:current server-sign.html (which produces the Option B / SHORT canonical&lt;br /&gt;
:that nGate-verify and v4call&amp;#039;s existing federation code both expect).&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;nGate-verify rejects with &amp;quot;attestation v4call_hive_account tag (X) ≠ post&amp;#039;s HIVE-ACCOUNT (Y)&amp;quot;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:You signed the attestation with one Hive account name (e.g.&lt;br /&gt;
:&amp;lt;code&amp;gt;hive-book&amp;lt;/code&amp;gt;) but the Hive post declares a different one (e.g.&lt;br /&gt;
:&amp;lt;code&amp;gt;hive-book.com&amp;lt;/code&amp;gt;). Re-sign with consistent values across the&lt;br /&gt;
:whole flow (server-sign → server-announce → Hive post).&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Sidecar can&amp;#039;t restart relay (&amp;quot;permission denied&amp;quot; on docker.sock)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Less common, but if the relay box has docker access locked down: either&lt;br /&gt;
:loosen perms on &amp;lt;code&amp;gt;/var/run/docker.sock&amp;lt;/code&amp;gt; (default world-writable&lt;br /&gt;
:on most distros) OR change &amp;lt;code&amp;gt;restart_command&amp;lt;/code&amp;gt; in ngate.yaml to&lt;br /&gt;
:a noop and restart the relay manually after each sync.&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;Sidecar in a restart loop&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:Probably an invalid &amp;lt;code&amp;gt;ngate.yaml&amp;lt;/code&amp;gt; or missing&lt;br /&gt;
:&amp;lt;code&amp;gt;node_modules&amp;lt;/code&amp;gt;. Check:&lt;br /&gt;
: &amp;lt;code&amp;gt;docker compose logs --tail=50 ngate-sync&amp;lt;/code&amp;gt;&lt;br /&gt;
:The startup log shows YAML parse errors clearly. If&lt;br /&gt;
:&amp;lt;code&amp;gt;node_modules&amp;lt;/code&amp;gt; is missing the sidecar will install it on first&lt;br /&gt;
:start; subsequent runs are fast.&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;&amp;quot;Restart cap hit&amp;quot; in logs&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:nGate refuses to restart the relay more than&lt;br /&gt;
:&amp;lt;code&amp;gt;max_restarts_per_day&amp;lt;/code&amp;gt; times in 24h. If you see this&lt;br /&gt;
:repeatedly, something upstream is causing the whitelist to flap.&lt;br /&gt;
:Investigate state.json and check whether candidates are missing-then-back&lt;br /&gt;
:repeatedly (suggests Hive RPC flakiness or a v4call-server post&lt;br /&gt;
:disappearing temporarily).&lt;br /&gt;
&lt;br /&gt;
;&amp;#039;&amp;#039;&amp;#039;&amp;quot;Sanity bound triggered&amp;quot; in logs&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:nGate refused to apply because the result would have removed every&lt;br /&gt;
:non-seed entry. Triggered when upstream had errors AND&lt;br /&gt;
:&amp;lt;code&amp;gt;--allow-removals&amp;lt;/code&amp;gt; was somehow set. With the sidecar&amp;#039;s&lt;br /&gt;
:PIPESTATUS-aware logic, this shouldn&amp;#039;t happen in practice; if it does,&lt;br /&gt;
:check upstream phase exit codes.&lt;br /&gt;
&lt;br /&gt;
== Removing nGate (back to manual whitelist) ==&lt;br /&gt;
&lt;br /&gt;
If you want to disable nGate and go back to hand-editing&lt;br /&gt;
&amp;lt;code&amp;gt;config.toml&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 cd /opt/nostr-relay&lt;br /&gt;
 docker compose down ngate-sync&lt;br /&gt;
 docker compose rm -f ngate-sync&lt;br /&gt;
&lt;br /&gt;
Then manually edit &amp;lt;code&amp;gt;config.toml&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 nano /opt/nostr-relay/config.toml&lt;br /&gt;
&lt;br /&gt;
Either:&lt;br /&gt;
&lt;br /&gt;
* Delete the BEGIN/END marker comments (keep the&lt;br /&gt;
  &amp;lt;code&amp;gt;[authorization]&amp;lt;/code&amp;gt; section between them as-is), OR&lt;br /&gt;
* Replace the entire managed block with a hand-curated whitelist.&lt;br /&gt;
&lt;br /&gt;
Restart the relay to pick up the new config:&lt;br /&gt;
&lt;br /&gt;
 docker compose restart nostr-relay&lt;br /&gt;
&lt;br /&gt;
The relay reads &amp;lt;code&amp;gt;config.toml&amp;lt;/code&amp;gt; on startup regardless of markers.&lt;br /&gt;
nGate&amp;#039;s markers are JUST for nGate&amp;#039;s internal &amp;quot;what to manage&amp;quot; logic; the&lt;br /&gt;
relay itself ignores them as TOML comments.&lt;br /&gt;
&lt;br /&gt;
== When you&amp;#039;re done ==&lt;br /&gt;
&lt;br /&gt;
After 24 hours of clean runs, nGate has handled at least one normal cycle&lt;br /&gt;
(scan + verify + gate + maybe-apply). At that point you can:&lt;br /&gt;
&lt;br /&gt;
* Stake some HIVE on your test operator to confirm a transition (off-gate&lt;br /&gt;
  → passing the gate after stake).&lt;br /&gt;
* Publish a v4call-server post for a new operator and watch it get picked&lt;br /&gt;
  up on the next cycle.&lt;br /&gt;
* Deploy nGate to your second relay box (nostr.v4call.com) with a&lt;br /&gt;
  staggered scan time (set &amp;lt;code&amp;gt;scan_interval_seconds&amp;lt;/code&amp;gt; the same but&lt;br /&gt;
  schedule the startup at a different time of day for faster&lt;br /&gt;
  federation-wide pickup).&lt;br /&gt;
&lt;br /&gt;
For the next phase of work (stage 3.6 refinements + stage 4 strfry&lt;br /&gt;
migration), see [[STATUS.md]] in the nGate repo.&lt;/div&gt;</description>
			<pubDate>Tue, 26 May 2026 00:38:07 GMT</pubDate>
			<dc:creator>AwesomO</dc:creator>
			<comments>https://www.completenoobs.com/noobs/Talk:Ngate_basic</comments>
		</item>
		<item>
			<title>V4call-server-data-flow</title>
			<link>https://www.completenoobs.com/noobs/index.php?title=V4call-server-data-flow&amp;diff=758&amp;oldid=0</link>
			<guid isPermaLink="false">https://www.completenoobs.com/noobs/index.php?title=V4call-server-data-flow&amp;diff=758&amp;oldid=0</guid>
			<description>&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;</description>
			<pubDate>Tue, 26 May 2026 00:36:05 GMT</pubDate>
			<dc:creator>AwesomO</dc:creator>
			<comments>https://www.completenoobs.com/noobs/Talk:V4call-server-data-flow</comments>
		</item>
		<item>
			<title>COMPLETENOOBS FUNDING</title>
			<link>https://www.completenoobs.com/noobs/index.php?title=COMPLETENOOBS_FUNDING&amp;diff=757&amp;oldid=742</link>
			<guid isPermaLink="false">https://www.completenoobs.com/noobs/index.php?title=COMPLETENOOBS_FUNDING&amp;diff=757&amp;oldid=742</guid>
			<description>&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 13:12, 22 May 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l2&quot;&gt;Line 2:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 2:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Bitcoin: &amp;quot;19S2thMqbYhwB6doLYQamStkzEyuqZs5Yn&amp;quot;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Bitcoin: &amp;quot;19S2thMqbYhwB6doLYQamStkzEyuqZs5Yn&amp;quot;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Bitcoin lightning: completenoobs@fountain.com&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Hive: cnoobs&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Hive: cnoobs&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</description>
			<pubDate>Fri, 22 May 2026 13:12:26 GMT</pubDate>
			<dc:creator>Noob</dc:creator>
			<comments>https://www.completenoobs.com/noobs/Talk:COMPLETENOOBS_FUNDING</comments>
		</item>
</channel></rss>