<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Pingtower</title><link>https://www.pingtower.com/</link><description>Recent content on Pingtower</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Thu, 24 Sep 2026 21:53:03 +0200</lastBuildDate><atom:link href="https://www.pingtower.com/index.xml" rel="self" type="application/rss+xml"/><item><title>API reference</title><link>https://www.pingtower.com/docs/reference/api/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/reference/api/</guid><description>&lt;p&gt;Every endpoint below answers at &lt;code&gt;https://api.pingtower.com&lt;/code&gt;, over TLS —
almost all of them with a bearer token. Which token you send decides which
plane you are on — the prefixes are mutually exclusive, so one kind can
never be mistaken for another.&lt;/p&gt;
&lt;p&gt;This page is generated from the daemon&amp;rsquo;s route table rather than written by
hand, and a build gate fails when the two disagree. Nothing here can outlive
the code it describes. The same document is served machine-readable at
&lt;a href="https://www.pingtower.com/api/openapi.json"&gt;/api/openapi.json&lt;/a&gt;, and this page as Markdown at
&lt;a href="https://www.pingtower.com/docs/reference/api/index.md"&gt;index.md&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>FAQ</title><link>https://www.pingtower.com/docs/help/faq/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/help/faq/</guid><description>&lt;h2 id="general"&gt;General&lt;/h2&gt;
&lt;details open&gt;&lt;summary&gt;Is Pingtower free?&lt;/summary&gt;
&lt;p&gt;Yes, during preview, with no credit card. The Free tier holds 5 projects, 1,000 alerts and 50,000 log rows per tenant. Paid plans will come later, announced well ahead, and early users get founder pricing. See &lt;a href="https://www.pingtower.com/docs/service/plans/"&gt;Plans &amp;amp; quotas&lt;/a&gt; and &lt;a href="https://www.pingtower.com/pricing/"&gt;pricing&lt;/a&gt;.&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;&lt;summary&gt;Do I need to install anything?&lt;/summary&gt;
&lt;p&gt;The iOS app, if you want a phone to ring. The service is hosted. The daemon and the addons agent are optional, for keeping data on your own hardware and for probing things from outside.&lt;/p&gt;</description></item><item><title>Ingest</title><link>https://www.pingtower.com/docs/service/ingest/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/service/ingest/</guid><description>&lt;p&gt;&lt;span class="pill post"&gt;POST&lt;/span&gt; &lt;code&gt;/v1/ingest&lt;/code&gt;, authenticated with a &lt;code&gt;pti_&lt;/code&gt; source token. Nothing in the body names a project or a source; the token decides.&lt;/p&gt;
&lt;h2 id="payload"&gt;Payload&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Field&lt;/th&gt;
					&lt;th&gt;Type&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;message&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;string&lt;/td&gt;
					&lt;td&gt;The line. Template extraction runs on this. Up to 64 KiB.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;level&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;integer&lt;/td&gt;
					&lt;td&gt;Severity. 100 debug, 200 info, 300 warn, 400 error, 500 critical.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;keys&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;object&lt;/td&gt;
					&lt;td&gt;Up to 128 named values, names up to 128 bytes, values up to 4096 bytes. Each value is substituted out of the message to form the template, is available to rule conditions, and can group alerts through &lt;code&gt;identifier_keys&lt;/code&gt;. Scalars only; they are stored as strings.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;tags&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;string[]&lt;/td&gt;
					&lt;td&gt;Up to 64 free-form labels a rule can match on, each up to 256 characters.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;payment gateway timeout after 30012ms for order o_48113&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;level&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;400&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;keys&amp;#34;&lt;/span&gt;: { &lt;span style="color:#cba6f7"&gt;&amp;#34;order&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;o_48113&amp;#34;&lt;/span&gt;, &lt;span style="color:#cba6f7"&gt;&amp;#34;gateway&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;stripe&amp;#34;&lt;/span&gt;, &lt;span style="color:#cba6f7"&gt;&amp;#34;latency_ms&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;30012&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;: [&lt;span style="color:#a6e3a1"&gt;&amp;#34;billing&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e3a1"&gt;&amp;#34;prod&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The server timestamps the line on arrival. There is no client timestamp field.&lt;/p&gt;</description></item><item><title>Install</title><link>https://www.pingtower.com/docs/addons/install/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/install/</guid><description>&lt;div class="docs-steps"&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Get a release and verify it&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Download the tarball and the &lt;code&gt;SHA256SUMS&lt;/code&gt; and &lt;code&gt;SHA256SUMS.sig&lt;/code&gt; beside it from the &lt;a href="https://www.pingtower.com/addons/#download"&gt;addons download table&lt;/a&gt;. Get the release public key, &lt;a href="https://www.pingtower.com/dist/addons/addons-release.pub"&gt;&lt;code&gt;addons-release.pub&lt;/code&gt;&lt;/a&gt;, and check the sums file was signed by this project, then that your tarball matches it. Skipping either lets a tampered tarball and a tampered checksum file agree with each other.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#89dceb"&gt;printf&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#39;release@pingtower.com namespaces=&amp;#34;pingtower-addons-release&amp;#34; %s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#cba6f7"&gt;$(&lt;/span&gt;cut -d&lt;span style="color:#a6e3a1"&gt;&amp;#39; &amp;#39;&lt;/span&gt; -f1,2 addons-release.pub&lt;span style="color:#cba6f7"&gt;)&lt;/span&gt;&lt;span style="color:#a6e3a1"&gt;&amp;#34;&lt;/span&gt; &amp;gt; allowed_signers
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-keygen -Y verify -f allowed_signers -I release@pingtower.com &lt;span style="color:#89b4fa"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -n pingtower-addons-release -s SHA256SUMS.sig &amp;lt; SHA256SUMS
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sha256sum --ignore-missing -c SHA256SUMS
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Addons signatures live in their own &lt;code&gt;pingtower-addons-release&lt;/code&gt; namespace, so an addons signature can never pass as a daemon release or vice versa. Needs stock OpenSSH 8.2 or later.&lt;/p&gt;</description></item><item><title>Install &amp; sign in</title><link>https://www.pingtower.com/docs/ios/install/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/ios/install/</guid><description>&lt;div class="docs-steps"&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install the app&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://testflight.apple.com/join/EBPRZ5De"&gt;Join the TestFlight beta&lt;/a&gt;.The beta is open to everyone, with no invite needed; when the app ships on the App Store the link changes and your account carries over.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sign up or sign in&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Email and password. Signing up creates an account and nothing else. Passwords are at least eight characters. The sign-in limiter allows a handful of attempts and then refills over about five minutes, so a mistyped password is not a lockout.&lt;/p&gt;</description></item><item><title>Install the daemon</title><link>https://www.pingtower.com/docs/self-hosting/install/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/self-hosting/install/</guid><description>&lt;div class="docs-steps"&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Download and verify&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Get the binary for your architecture from the &lt;a href="https://www.pingtower.com/get-started/#self-host"&gt;download table&lt;/a&gt;. Every release carries an SSH signature over a signed provenance file that names exactly what was built. Check the SHA-256 against the table, then verify the signature against the release public key, &lt;a href="https://www.pingtower.com/get-started/#self-host"&gt;&lt;code&gt;pingtower-release.pub&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sha256sum pingtower-linux-amd64 &lt;span style="color:#6c7086;font-style:italic"&gt;# compare with the table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#89dceb"&gt;printf&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#39;release@pingtower.com namespaces=&amp;#34;pingtower-release&amp;#34; %s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#cba6f7"&gt;$(&lt;/span&gt;cut -d&lt;span style="color:#a6e3a1"&gt;&amp;#39; &amp;#39;&lt;/span&gt; -f1,2 pingtower-release.pub&lt;span style="color:#cba6f7"&gt;)&lt;/span&gt;&lt;span style="color:#a6e3a1"&gt;&amp;#34;&lt;/span&gt; &amp;gt; allowed_signers
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-keygen -Y verify -f allowed_signers -I release@pingtower.com &lt;span style="color:#89b4fa"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -n pingtower-release -s pingtower-linux-amd64.release.sig &lt;span style="color:#89b4fa"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt; pingtower-linux-amd64.release
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat pingtower-linux-amd64.release &lt;span style="color:#6c7086;font-style:italic"&gt;# sha256= must match the sum above&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Needs stock OpenSSH 8.2 or later. If any check fails, do not run the binary.&lt;/p&gt;</description></item><item><title>Quickstart</title><link>https://www.pingtower.com/docs/getting-started/quickstart/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/getting-started/quickstart/</guid><description>&lt;div class="docs-steps"&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create an account and a tenant&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Sign up in the &lt;a href="https://www.pingtower.com/docs/ios/install/"&gt;iOS app&lt;/a&gt;, or from the API. Signing up gives you a login; it does not create a tenant, so create one next. A tenant is a company or a team, and everything below lives inside it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f5e0dc"&gt;PT&lt;/span&gt;&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;https://api.pingtower.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -s &lt;span style="color:#a6e3a1"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#f5e0dc"&gt;$PT&lt;/span&gt;&lt;span style="color:#a6e3a1"&gt;/v1/signup&amp;#34;&lt;/span&gt; -H &lt;span style="color:#a6e3a1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -d &lt;span style="color:#a6e3a1"&gt;&amp;#39;{&amp;#34;email&amp;#34;:&amp;#34;you@example.com&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;…&amp;#34;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# → {&amp;#34;account&amp;#34;:{…},&amp;#34;token&amp;#34;:&amp;#34;pts_…&amp;#34;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -s &lt;span style="color:#a6e3a1"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#f5e0dc"&gt;$PT&lt;/span&gt;&lt;span style="color:#a6e3a1"&gt;/v1/tenants&amp;#34;&lt;/span&gt; -H &lt;span style="color:#a6e3a1"&gt;&amp;#34;Authorization: Bearer pts_…&amp;#34;&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#a6e3a1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; -d &lt;span style="color:#a6e3a1"&gt;&amp;#39;{&amp;#34;name&amp;#34;:&amp;#34;Acme&amp;#34;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# → {&amp;#34;id&amp;#34;:&amp;#34;&amp;lt;tid&amp;gt;&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Acme&amp;#34;,&amp;#34;role&amp;#34;:&amp;#34;owner&amp;#34;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mint a tenant API key&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Concepts</title><link>https://www.pingtower.com/docs/getting-started/concepts/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/getting-started/concepts/</guid><description>&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Term&lt;/th&gt;
					&lt;th&gt;What it is&lt;/th&gt;
					&lt;th&gt;Lives in&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Account&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;You. An email and a password. One account can belong to many tenants.&lt;/td&gt;
					&lt;td&gt;pingtower.com&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Tenant&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;A company or a team. Owns its own data, members and API keys. Every tenant you join gives you a separate credential scoped to just that tenant&amp;rsquo;s data.&lt;/td&gt;
					&lt;td&gt;pingtower.com, or your own box&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Project&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;A namespace inside a tenant: a service, an environment, a product. Holds sources, rules, integrations, actions and reports. Open-alert counts roll up per project. Addressed by name.&lt;/td&gt;
					&lt;td&gt;tenant&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;One thing that sends lines. Each source has its own ingest token, which you can rotate without touching the others, and its own &lt;code&gt;retain_logs&lt;/code&gt; switch.&lt;/td&gt;
					&lt;td&gt;project&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Template&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;A message&amp;rsquo;s shape: the line with the values you passed as &lt;code&gt;keys&lt;/code&gt; substituted out. &lt;code&gt;payment failed for o_48113&lt;/code&gt; with &lt;code&gt;keys.order = o_48113&lt;/code&gt; becomes &lt;code&gt;payment failed for {{ .order }}&lt;/code&gt;, so every order&amp;rsquo;s failure counts as the same thing.&lt;/td&gt;
					&lt;td&gt;project&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Rule&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;A declarative match plus timing: what to look for, how many in what window, how often to re-page, who to escalate to.&lt;/td&gt;
					&lt;td&gt;project&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Alert&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;A rule that fired. Carries a count, an escalation level, first and last seen, and the last message. Firing until acked, resolved when done.&lt;/td&gt;
					&lt;td&gt;project&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="how-isolation-works"&gt;How isolation works&lt;/h2&gt;
&lt;p&gt;Each tenant is one SQLite file. The credential you present decides which file is opened. Nothing in a URL or a request body can reach another tenant&amp;rsquo;s data, because there is no code path that opens a file the token did not name.&lt;/p&gt;</description></item><item><title>Configure</title><link>https://www.pingtower.com/docs/addons/config/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/config/</guid><description>&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# /etc/addons/agent.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cba6f7"&gt;agent&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;id&lt;/span&gt;: probe-fra-01
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;region&lt;/span&gt;: eu-fra
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;tower&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;url&lt;/span&gt;: https://api.pingtower.com &lt;span style="color:#6c7086;font-style:italic"&gt;# or your own box&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;token_env&lt;/span&gt;: PINGTOWER_INGEST_TOKEN &lt;span style="color:#6c7086;font-style:italic"&gt;# read from /etc/addons/agent.env&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;heartbeat&lt;/span&gt;: 5m
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;max_concurrent&lt;/span&gt;: &lt;span style="color:#fab387"&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;enable_addons&lt;/span&gt;: [sshcheck] &lt;span style="color:#6c7086;font-style:italic"&gt;# privileged addons must be listed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;enable_sensors&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cba6f7"&gt;defaults&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;interval&lt;/span&gt;: 60s
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;sample_interval&lt;/span&gt;: 60s
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;timeout&lt;/span&gt;: 10s
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;report&lt;/span&gt;: on_change
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cba6f7"&gt;checks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#cba6f7"&gt;id&lt;/span&gt;: api-health
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;addon&lt;/span&gt;: httpcheck
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;interval&lt;/span&gt;: 30s
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;down_level&lt;/span&gt;: &lt;span style="color:#fab387"&gt;500&lt;/span&gt; &lt;span style="color:#6c7086;font-style:italic"&gt;# 500 pages; 400 is the default&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;url&lt;/span&gt;: https://api.acme.example/healthz
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;expect_status&lt;/span&gt;: &lt;span style="color:#fab387"&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;max_ttfb&lt;/span&gt;: 800ms
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#cba6f7"&gt;id&lt;/span&gt;: api-cert
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;addon&lt;/span&gt;: tlscert
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;interval&lt;/span&gt;: 6h
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;address&lt;/span&gt;: api.acme.example:443
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;warn_before&lt;/span&gt;: 336h &lt;span style="color:#6c7086;font-style:italic"&gt;# 14 days; default is 21&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#cba6f7"&gt;id&lt;/span&gt;: box-login
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;addon&lt;/span&gt;: sshcheck
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;interval&lt;/span&gt;: 5m
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;address&lt;/span&gt;: &lt;span style="color:#fab387"&gt;10.0.0.12&lt;/span&gt;:&lt;span style="color:#fab387"&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;user&lt;/span&gt;: probe
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;key_file&lt;/span&gt;: /etc/addons/probe_ed25519
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;command&lt;/span&gt;: systemctl is-active postgresql
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;expect_output&lt;/span&gt;: active
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="agent"&gt;&lt;code&gt;agent&lt;/code&gt;&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Field&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Stable name. Carried on every event and the heartbeat&amp;rsquo;s identity.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;region&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Free-form label carried on every event.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;tower.url&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Where to POST. &lt;code&gt;https://api.pingtower.com&lt;/code&gt;, or your box.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;tower.token_env&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;PINGTOWER_INGEST_TOKEN&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Environment variable holding the &lt;code&gt;pti_&lt;/code&gt; token. Never a config value, so it stays out of the file.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;tower.max_rps&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Cap on the agent&amp;rsquo;s own send rate.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;tower.spool_dir&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/var/lib/addons/spool&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Where events wait out an outage.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;tower.spool_max_bytes&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;64 MiB&lt;/td&gt;
					&lt;td&gt;Oldest events are dropped past this.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;heartbeat&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;5m&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;How often the agent reports on itself.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_concurrent&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;8&lt;/td&gt;
					&lt;td&gt;Checks running at once.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;enable_addons&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Privileged addons this agent may run. An unknown or non-privileged name is a startup error.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;enable_sensors&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Sensors this agent may run. Same rule.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;state_dir&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;beside the spool&lt;/td&gt;
					&lt;td&gt;Where addons that need a baseline, like &lt;code&gt;hostmetrics&lt;/code&gt;, keep it.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="defaults-and-per-check-fields"&gt;&lt;code&gt;defaults&lt;/code&gt; and per-check fields&lt;/h2&gt;
&lt;p&gt;Every field here can be set once under &lt;code&gt;defaults&lt;/code&gt; and overridden per check.&lt;/p&gt;</description></item><item><title>Errors</title><link>https://www.pingtower.com/docs/reference/errors/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/reference/errors/</guid><description>&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{ &lt;span style="color:#cba6f7"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;validation failed&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;: [ { &lt;span style="color:#cba6f7"&gt;&amp;#34;field&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;, &lt;span style="color:#cba6f7"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;must not be empty&amp;#34;&lt;/span&gt;, &lt;span style="color:#cba6f7"&gt;&amp;#34;rule&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;required&amp;#34;&lt;/span&gt; } ] }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Status&lt;/th&gt;
					&lt;th&gt;When&lt;/th&gt;
					&lt;th&gt;Extra fields&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;400&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The body or a parameter failed validation.&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;fields[]&lt;/code&gt; with &lt;code&gt;field&lt;/code&gt;, &lt;code&gt;message&lt;/code&gt;, &lt;code&gt;rule&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;401&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Bearer missing, malformed, or not the kind this route takes; or a wrong &lt;code&gt;current_password&lt;/code&gt; on a route that re-checks it.&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;403&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Your role cannot do this: a member renaming a project or deleting a source, an admin inviting an owner.&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;404&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Unknown project, source, rule, alert, or a tenant you are not a member of.&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;409&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Conflict. Deleting something a rule still names; removing the last owner; an email already taken; a disabled integration asked to test; editing a self-hosted box&amp;rsquo;s relay integration through anything but &lt;code&gt;PUT /v1/relay&lt;/code&gt;.&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;rules[]&lt;/code&gt; for a referenced source, action or schedule.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;422&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The request was well-formed but points at something that does not exist: a rule naming an unknown action, a schedule naming a non-member. Also an integration named &lt;code&gt;pingtower.com relay&lt;/code&gt;, which is reserved.&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;errors[]&lt;/code&gt; when more than one.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;429&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Over a rate limit, a storage quota, or a count cap.&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Retry-After&lt;/code&gt; header, whole seconds, on a rate limit only.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;500&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The daemon could not store or read.&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;503&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;A test route was called on a daemon with no sender or executor configured.&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="the-referenced-error-shape"&gt;The referenced-error shape&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{ &lt;span style="color:#cba6f7"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;store: source is referenced by rules: gateway-timeouts, errors&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;rules&amp;#34;&lt;/span&gt;: [&lt;span style="color:#a6e3a1"&gt;&amp;#34;gateway-timeouts&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e3a1"&gt;&amp;#34;errors&amp;#34;&lt;/span&gt;] }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Returned on &lt;code&gt;DELETE&lt;/code&gt; of a source, action or on-call schedule, and on renaming an action, while a rule still references it. Edit or delete those rules first.&lt;/p&gt;</description></item><item><title>Reverse proxy &amp; TLS</title><link>https://www.pingtower.com/docs/self-hosting/proxy/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/self-hosting/proxy/</guid><description>&lt;h2 id="caddy"&gt;Caddy&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/etc/caddy/Caddyfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alerts.example.com {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; reverse_proxy 127.0.0.1:8391 {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; header_up X-Real-IP {remote_host}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Caddy issues and renews the certificate itself. The &lt;code&gt;header_up&lt;/code&gt; line is for &lt;a href="#client-addresses"&gt;client addresses&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="nginx"&gt;nginx&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cba6f7"&gt;server&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#94e2d5"&gt;listen&lt;/span&gt; &lt;span style="color:#fab387"&gt;443&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;ssl&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;http2&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#94e2d5"&gt;server_name&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;alerts.example.com&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#94e2d5"&gt;ssl_certificate&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;/etc/letsencrypt/live/alerts.example.com/fullchain.pem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#94e2d5"&gt;ssl_certificate_key&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;/etc/letsencrypt/live/alerts.example.com/privkey.pem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#94e2d5"&gt;location&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;/&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#94e2d5"&gt;proxy_pass&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;http://127.0.0.1:8391&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#94e2d5"&gt;proxy_http_version&lt;/span&gt; &lt;span style="color:#fab387"&gt;1&lt;/span&gt;&lt;span style="color:#a6e3a1"&gt;.1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#94e2d5"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;Host&lt;/span&gt; &lt;span style="color:#f5e0dc"&gt;$host&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#94e2d5"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;X-Real-IP&lt;/span&gt; &lt;span style="color:#f5e0dc"&gt;$remote_addr&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#94e2d5"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;X-Forwarded-For&lt;/span&gt; &lt;span style="color:#f5e0dc"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#94e2d5"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;https&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#94e2d5"&gt;proxy_read_timeout&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;60s&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="docs-callout warn"&gt;&lt;svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"&gt;&lt;path d="M8 2 1.5 13.5h13L8 2Z"/&gt;&lt;path d="M8 6.5v3M8 11.5v.5"/&gt;&lt;/svg&gt;&lt;div&gt;&lt;span class="t"&gt;Long-poll needs a patient proxy&lt;/span&gt;&lt;code&gt;GET /v1/pull?wait=25&lt;/code&gt; holds the connection open for up to 25 seconds. A proxy read timeout under 30 seconds turns every quiet poll into a 504. Caddy&amp;rsquo;s default is fine; nginx&amp;rsquo;s default of 60 seconds is fine; a stricter default in front of either is not.&lt;/div&gt;
&lt;/div&gt;

&lt;h2 id="client-addresses"&gt;Client addresses&lt;/h2&gt;
&lt;p&gt;Sign-up, sign-in, account deletion, password-reset requests and seat redemption are rate-limited per client address. Behind a proxy on the same host, every request reaches the daemon from &lt;code&gt;127.0.0.1&lt;/code&gt;, so the daemon cannot tell callers apart: it skips the per-client limit altogether, keeps the per-email limits, and counts everyone&amp;rsquo;s failed sign-ins as one caller&amp;rsquo;s. Set &lt;code&gt;-client-ip-header X-Real-IP&lt;/code&gt; (or &lt;code&gt;PINGTOWER_CLIENT_IP_HEADER=X-Real-IP&lt;/code&gt;) to the header the proxy sets, as both examples above do.&lt;/p&gt;</description></item><item><title>Rules</title><link>https://www.pingtower.com/docs/service/rules/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/service/rules/</guid><description>&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;gateway-timeouts&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;min_level&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;400&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;sources&amp;#34;&lt;/span&gt;: [&lt;span style="color:#a6e3a1"&gt;&amp;#34;api&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e3a1"&gt;&amp;#34;worker&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;: [&lt;span style="color:#a6e3a1"&gt;&amp;#34;billing&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;template_contains&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;gateway timeout&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;conditions&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; { &lt;span style="color:#cba6f7"&gt;&amp;#34;key&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;latency_ms&amp;#34;&lt;/span&gt;, &lt;span style="color:#cba6f7"&gt;&amp;#34;op&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;gte&amp;#34;&lt;/span&gt;, &lt;span style="color:#cba6f7"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;10000&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;fire_after&amp;#34;&lt;/span&gt;: { &lt;span style="color:#cba6f7"&gt;&amp;#34;count&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;5&lt;/span&gt;, &lt;span style="color:#cba6f7"&gt;&amp;#34;window_minutes&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;10&lt;/span&gt;, &lt;span style="color:#cba6f7"&gt;&amp;#34;distinct_sources&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;true&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;identifier_keys&amp;#34;&lt;/span&gt;: [&lt;span style="color:#a6e3a1"&gt;&amp;#34;gateway&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;dedup_window_minutes&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;30&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;renotify_minutes&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;10&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;max_renotify&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;6&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;escalations&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; { &lt;span style="color:#cba6f7"&gt;&amp;#34;after_minutes&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;15&lt;/span&gt;, &lt;span style="color:#cba6f7"&gt;&amp;#34;schedules&amp;#34;&lt;/span&gt;: [&lt;span style="color:#a6e3a1"&gt;&amp;#34;backend-secondary&amp;#34;&lt;/span&gt;], &lt;span style="color:#cba6f7"&gt;&amp;#34;repeat_minutes&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;10&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; { &lt;span style="color:#cba6f7"&gt;&amp;#34;after_minutes&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;45&lt;/span&gt;, &lt;span style="color:#cba6f7"&gt;&amp;#34;accounts&amp;#34;&lt;/span&gt;: [&lt;span style="color:#a6e3a1"&gt;&amp;#34;cto@acme.example&amp;#34;&lt;/span&gt;] }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;actions&amp;#34;&lt;/span&gt;: [&lt;span style="color:#a6e3a1"&gt;&amp;#34;restart-gateway-worker&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;auto_resolve_quiet_minutes&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;60&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;investigate&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;span class="pill post"&gt;POST&lt;/span&gt; &lt;code&gt;/v1/projects/{id}/rules&lt;/code&gt; creates one. &lt;span class="pill get"&gt;GET&lt;/span&gt; &lt;span class="pill put"&gt;PUT&lt;/span&gt; &lt;span class="pill del"&gt;DELETE&lt;/span&gt; &lt;code&gt;/v1/projects/{id}/rules/{name}&lt;/code&gt; read, replace and remove it. A rule&amp;rsquo;s name is its identity. A project holds at most 500 rules, and every &lt;code&gt;…_minutes&lt;/code&gt; field is capped at 525,600, a year; the rest are in &lt;a href="https://www.pingtower.com/docs/reference/limits/#sizes"&gt;Limits&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>The alert feed</title><link>https://www.pingtower.com/docs/ios/feed/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/ios/feed/</guid><description>&lt;h2 id="the-list"&gt;The list&lt;/h2&gt;
&lt;p&gt;The feed is a single live pull over the tenant&amp;rsquo;s alert cursor, so it updates in place without a refresh. Unresolved alerts sort before resolved ones, and within that the most recently seen first.&lt;/p&gt;
&lt;p&gt;Filter by state with the segmented control: &lt;strong&gt;Open&lt;/strong&gt; is everything not resolved, then &lt;strong&gt;Firing&lt;/strong&gt;, &lt;strong&gt;Acked&lt;/strong&gt;, &lt;strong&gt;Resolved&lt;/strong&gt;. Filter by project from the projects already in the list. Pull down to restart the poll if the connection dropped.&lt;/p&gt;</description></item><item><title>Troubleshooting</title><link>https://www.pingtower.com/docs/help/troubleshooting/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/help/troubleshooting/</guid><description>&lt;h2 id="lines-arrive-but-no-alert-opens"&gt;Lines arrive but no alert opens&lt;/h2&gt;
&lt;p&gt;Check the ingest response: &lt;code&gt;&amp;quot;alerted&amp;quot;: false&lt;/code&gt; means no rule matched. The usual causes, in order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The rule&amp;rsquo;s &lt;code&gt;min_level&lt;/code&gt; is above the line&amp;rsquo;s level. &lt;code&gt;min_level: 400&lt;/code&gt; ignores a 300.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match.sources&lt;/code&gt; names a source and the line came from another.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;template_contains&lt;/code&gt; is matched against the template, not the message. If the substring you wrote contains a value that is in &lt;code&gt;keys&lt;/code&gt;, it has been replaced with &lt;code&gt;{{ .name }}&lt;/code&gt; in the template.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fire_after&lt;/code&gt; is waiting for more lines.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;GET /v1/projects/{id}/rules/{name}&lt;/code&gt; shows the rule as stored.&lt;/p&gt;</description></item><item><title>Alerts &amp; escalation</title><link>https://www.pingtower.com/docs/service/alerts/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/service/alerts/</guid><description>&lt;div class="docs-flow"&gt;&lt;span class="st open"&gt;firing&lt;/span&gt;&lt;span class="ar"&gt;→&lt;/span&gt;&lt;span class="st ack"&gt;acked&lt;/span&gt;&lt;span class="ar"&gt;→&lt;/span&gt;&lt;span class="st res"&gt;resolved&lt;/span&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Firing&lt;/strong&gt; means nobody has claimed it. It re-pages on its rule&amp;rsquo;s cadence and climbs its escalation ladder. In the app this is the Open filter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Acked&lt;/strong&gt; means someone is on it. Paging stops. The count keeps climbing if lines keep matching, so you can watch whether your fix worked.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resolved&lt;/strong&gt; means done. The alert leaves the open feed, stops counting toward the project&amp;rsquo;s &lt;code&gt;open_alerts&lt;/code&gt;, and stays in history for thirty days by default.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-an-alert-carries"&gt;What an alert carries&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Field&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Stable id, used by ack and resolve.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;project&lt;/code&gt;, &lt;code&gt;rule&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Which rule opened it.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;state&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;firing&lt;/code&gt;, &lt;code&gt;acked&lt;/code&gt; or &lt;code&gt;resolved&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;level&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The highest level seen among its lines.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;count&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Matching lines since it opened.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;escalation_level&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Which rung of the ladder it has reached. 0 is the rule&amp;rsquo;s own recipients.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;template&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The recovered shape that matched.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;last_message&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The most recent raw line, so the notification says something useful.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;first_seen&lt;/code&gt;, &lt;code&gt;last_seen&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;When it opened and when the last line matched.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;identifiers&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The key values that split this alert from its siblings, when the rule uses &lt;code&gt;identifier_keys&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;page_accounts&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Who the current escalation rung is paging, when it narrows the audience.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;investigate&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The rule asked the app to lead with the log tail.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="reading-the-feed"&gt;Reading the feed&lt;/h2&gt;
&lt;p&gt;&lt;span class="pill get"&gt;GET&lt;/span&gt; &lt;code&gt;/v1/pull?cursor=N&amp;amp;wait=S&lt;/code&gt; returns every alert changed since cursor &lt;code&gt;N&lt;/code&gt; and the next cursor. &lt;code&gt;wait&lt;/code&gt; parks the request for up to that many seconds if nothing has changed, which is how the app keeps a live feed without polling in a loop. &lt;code&gt;wait=0&lt;/code&gt; returns immediately.&lt;/p&gt;</description></item><item><title>Credentials</title><link>https://www.pingtower.com/docs/getting-started/credentials/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/getting-started/credentials/</guid><description>&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Token&lt;/th&gt;
					&lt;th&gt;Prefix&lt;/th&gt;
					&lt;th&gt;Grants&lt;/th&gt;
					&lt;th&gt;Get one from&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Session&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;pts_&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The account plane: list and create tenants, manage members and keys.&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;POST /v1/login&lt;/code&gt;, or signing in from the app&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Tenant API key&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;ptk_&amp;lt;tenant&amp;gt;_&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;One tenant&amp;rsquo;s data plane: pull, ack, resolve, projects, sources, rules, integrations, actions, log tail.&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;POST /v1/tenants/{tid}/keys&lt;/code&gt;, or Account → API keys in the app&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Ingest source token&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;pti_&amp;lt;tenant&amp;gt;_&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;POST /v1/ingest&lt;/code&gt; only. Cannot read anything back.&lt;/td&gt;
					&lt;td&gt;Creating a source&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;tenant&amp;gt;&lt;/code&gt; segment is a routing hint that picks which database to open. The secret is still verified by a constant-time compare inside that database, so a forged tenant segment opens the wrong file and fails.&lt;/p&gt;</description></item><item><title>Critical alerts</title><link>https://www.pingtower.com/docs/ios/critical/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/ios/critical/</guid><description>&lt;h2 id="which-pushes-are-critical"&gt;Which pushes are critical&lt;/h2&gt;
&lt;p&gt;The event decides, not the level. Two events are delivered as critical alerts: &lt;code&gt;alert.opened&lt;/code&gt;, the first page of something nobody has seen yet, and &lt;code&gt;alert.escalated&lt;/code&gt;, meaning a rule&amp;rsquo;s renotify fired or a ladder rung was reached without an ack. A repeat of an open alert (&lt;code&gt;alert.bumped&lt;/code&gt;), an ack and a resolve are ordinary notifications, so the interruption is spent once per new alert rather than once per matching line.&lt;/p&gt;</description></item><item><title>Rate limits</title><link>https://www.pingtower.com/docs/reference/limits/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/reference/limits/</guid><description>&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Plane&lt;/th&gt;
					&lt;th style="text-align: right"&gt;Free&lt;/th&gt;
					&lt;th style="text-align: right"&gt;Pro&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Ingest, per tenant&lt;/td&gt;
					&lt;td style="text-align: right"&gt;20 req/s, burst 40&lt;/td&gt;
					&lt;td style="text-align: right"&gt;200 req/s, burst 400&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Data and account, per tenant&lt;/td&gt;
					&lt;td style="text-align: right"&gt;10 req/s, burst 20&lt;/td&gt;
					&lt;td style="text-align: right"&gt;100 req/s, burst 200&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Enterprise limits are set per tenant. Over a limit the response is &lt;code&gt;429&lt;/code&gt; with &lt;code&gt;Retry-After&lt;/code&gt; in whole seconds.&lt;/p&gt;
&lt;h2 id="routes-that-take-a-password"&gt;Routes that take a password&lt;/h2&gt;
&lt;p&gt;Sign-up, login, account deletion and password-reset requests are limited in layers, so that one caller who knows your email cannot lock you out:&lt;/p&gt;</description></item><item><title>Relay &amp; pairing</title><link>https://www.pingtower.com/docs/self-hosting/relay/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/self-hosting/relay/</guid><description>&lt;h2 id="the-shape-of-it"&gt;The shape of it&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;The box mints a join code: &lt;code&gt;pingtower pair --email you@example.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The app redeems the code against the box and gets a box-minted &lt;code&gt;ptk_&lt;/code&gt; key, plus a receipt.&lt;/li&gt;
&lt;li&gt;pingtower.com confirms the receipt with the box, once, at &lt;code&gt;GET /v1/pair/verify&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The app writes the relay credential to the box with &lt;code&gt;PUT /v1/relay&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;From then on, every project on the box has a webhook integration named &amp;ldquo;pingtower.com relay&amp;rdquo;. Every alert event goes out through it like any other webhook, and pingtower.com turns it into a push to the devices paired with that tenant, &lt;a href="https://www.pingtower.com/docs/ios/critical/"&gt;critical&lt;/a&gt; or ordinary by the same rule as a hosted page.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The box never accepts a connection from pingtower.com beyond the one-time verify. Everything is outbound from the box, so it works from behind NAT as long as the app can reach the box&amp;rsquo;s URL when it pairs.&lt;/p&gt;</description></item><item><title>Log tail &amp; templates</title><link>https://www.pingtower.com/docs/service/logtail/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/service/logtail/</guid><description>&lt;h2 id="templates"&gt;Templates&lt;/h2&gt;
&lt;p&gt;A template is a message with the values you passed as &lt;code&gt;keys&lt;/code&gt; substituted out. The substitution is literal: each key&amp;rsquo;s value is searched for in the message and replaced with &lt;code&gt;{{ .name }}&lt;/code&gt;, longest value first, every occurrence.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Message&lt;/th&gt;
					&lt;th&gt;Keys&lt;/th&gt;
					&lt;th&gt;Template&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;user bob logged in&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;{&amp;quot;name&amp;quot;:&amp;quot;bob&amp;quot;}&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;user {{ .name }} logged in&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;timeout after 30012ms for o_48113&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;{&amp;quot;order&amp;quot;:&amp;quot;o_48113&amp;quot;,&amp;quot;latency_ms&amp;quot;:30012}&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;timeout after {{ .latency_ms }}ms for {{ .order }}&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;disk full&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;none&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;disk full&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The template&amp;rsquo;s SHA-256 is its id, returned on every ingest response as &lt;code&gt;template_id&lt;/code&gt; and carried by every alert as &lt;code&gt;template&lt;/code&gt;. Two lines with the same template are the same thing to a rule and fold into the same alert.&lt;/p&gt;</description></item><item><title>Pairing your own box</title><link>https://www.pingtower.com/docs/ios/pairing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/ios/pairing/</guid><description>&lt;p&gt;Pairing joins two things: an account on pingtower.com, which is what the app signs in with, and a tenant on a box you run, which is where the data lives. Once paired, the app talks to the box for everything and to pingtower.com only for the account and for push.&lt;/p&gt;
&lt;div class="docs-steps"&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;On the box, mint a join code&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pingtower pair --email you@example.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It prints one code starting with &lt;code&gt;ptj_&lt;/code&gt;. The code bundles the box&amp;rsquo;s URL, the tenant, and a one-shot pairing secret. It is valid for one hour and can be redeemed once. Owners and admins can also mint codes from the API with &lt;code&gt;POST /v1/pair/codes&lt;/code&gt;, with &lt;code&gt;ttl_seconds&lt;/code&gt; up to seven days, to seat teammates without a shell. Seats exist only on a box: pingtower.com refuses to mint or redeem one with a &lt;code&gt;409&lt;/code&gt;, and a hosted tenant adds people by invite.&lt;/p&gt;</description></item><item><title>Projects &amp; sources</title><link>https://www.pingtower.com/docs/service/projects/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/service/projects/</guid><description>&lt;h2 id="projects"&gt;Projects&lt;/h2&gt;
&lt;p&gt;&lt;span class="pill get"&gt;GET&lt;/span&gt; &lt;code&gt;/v1/projects&lt;/code&gt; lists every project the tenant owns, name-ordered, each with &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;config_version&lt;/code&gt;, &lt;code&gt;sources&lt;/code&gt;, &lt;code&gt;rules&lt;/code&gt;, &lt;code&gt;open_alerts&lt;/code&gt; and &lt;code&gt;created_at&lt;/code&gt;. Paginate with &lt;code&gt;limit&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt;, passing the last name on the previous page.&lt;/p&gt;
&lt;p&gt;&lt;span class="pill post"&gt;POST&lt;/span&gt; &lt;code&gt;/v1/projects&lt;/code&gt; with &lt;code&gt;{&amp;quot;name&amp;quot;: &amp;quot;…&amp;quot;}&lt;/code&gt; creates one. Projects are explicit rows: a typo in a path is a &lt;code&gt;404&lt;/code&gt;, never a project created by accident.&lt;/p&gt;
&lt;p&gt;&lt;span class="pill post"&gt;POST&lt;/span&gt; &lt;code&gt;/v1/projects/{id}/rename&lt;/code&gt; and &lt;span class="pill del"&gt;DELETE&lt;/span&gt; &lt;code&gt;/v1/projects/{id}&lt;/code&gt; are owner or admin only. Renaming does not break ingest tokens, which resolve through the source row. Deleting removes the project&amp;rsquo;s sources, rules, alerts and reports; a report&amp;rsquo;s public page stops resolving.&lt;/p&gt;</description></item><item><title>Integrations</title><link>https://www.pingtower.com/docs/service/integrations/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/service/integrations/</guid><description>&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Kind&lt;/th&gt;
					&lt;th&gt;Needs&lt;/th&gt;
					&lt;th&gt;Notes&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;webhook&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;url&lt;/code&gt;. Optional &lt;code&gt;secret&lt;/code&gt;; one is minted and shown once if you leave it out.&lt;/td&gt;
					&lt;td&gt;Signed. Payload below.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;slack&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;webhook_url&lt;/code&gt; from an incoming-webhook app.&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;telegram&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;token&lt;/code&gt; for a bot and a &lt;code&gt;chat_id&lt;/code&gt;.&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;apns&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Nothing. Rejected if you send credentials.&lt;/td&gt;
					&lt;td&gt;Push to the iOS app. Recipients are the tenant&amp;rsquo;s members; the row exists so you can filter its events and read its delivery log.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Credentials, and a webhook&amp;rsquo;s signing secret, are sealed at rest and never returned by a read. Set &lt;code&gt;-secret-key&lt;/code&gt; on a self-hosted daemon before creating any.&lt;/p&gt;</description></item><item><title>Actions</title><link>https://www.pingtower.com/docs/service/actions/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/service/actions/</guid><description>&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;restart-gateway-worker&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;method&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;https://deploy.acme.example/api/services/gateway-worker/restart&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;headers&amp;#34;&lt;/span&gt;: { &lt;span style="color:#cba6f7"&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;Bearer …&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;{\&amp;#34;reason\&amp;#34;:\&amp;#34;pingtower\&amp;#34;}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;timeout_seconds&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;10&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;events&amp;#34;&lt;/span&gt;: [&lt;span style="color:#a6e3a1"&gt;&amp;#34;alert.opened&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;: &lt;span style="color:#fab387"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Field&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Identity. Rules reference it by name or id.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;method&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt; or &lt;code&gt;DELETE&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;url&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;http or https, up to 2048 characters. Private and loopback ranges are refused.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;headers&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Up to 32. Sealed at rest; reads return only &lt;code&gt;header_keys&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;body&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Up to 8 KiB. Sealed at rest; reads return &lt;code&gt;has_body&lt;/code&gt;. Send &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt; to clear it, omit it to keep it.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;timeout_seconds&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;0 to 60, default 10.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;events&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;alert.opened&lt;/code&gt;, &lt;code&gt;alert.escalated&lt;/code&gt;, or both. Default &lt;code&gt;alert.opened&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;enabled&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;A disabled action is skipped silently.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;span class="pill post"&gt;POST&lt;/span&gt; &lt;span class="pill get"&gt;GET&lt;/span&gt; &lt;code&gt;/v1/projects/{id}/actions&lt;/code&gt;, then &lt;span class="pill get"&gt;GET&lt;/span&gt; &lt;span class="pill put"&gt;PUT&lt;/span&gt; &lt;span class="pill del"&gt;DELETE&lt;/span&gt; &lt;code&gt;/v1/projects/{id}/actions/{aid}&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>On-call schedules</title><link>https://www.pingtower.com/docs/service/oncall/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/service/oncall/</guid><description>&lt;div class="docs-callout note"&gt;&lt;svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"&gt;&lt;circle cx="8" cy="8" r="6.5"/&gt;&lt;path d="M8 7v4M8 5v.5"/&gt;&lt;/svg&gt;&lt;div&gt;&lt;span class="t"&gt;API only for now&lt;/span&gt;Schedules, overrides and the who-is-on-call-now answer are served by the hosted service and by self-hosted boxes alike. The iOS app does not yet have screens for them; manage them from the API until it does.&lt;/div&gt;
&lt;/div&gt;

&lt;h2 id="layers-and-rotations"&gt;Layers and rotations&lt;/h2&gt;
&lt;p&gt;A schedule is per project. It has a timezone and one or more layers. Each layer rotates through its participants every &lt;code&gt;shift_minutes&lt;/code&gt;, starting at &lt;code&gt;rotation_start&lt;/code&gt;. A layer with a &lt;code&gt;restriction&lt;/code&gt; is active only inside that window, and a later layer wins over an earlier one while both are active. That is how &amp;ldquo;weekday office hours go to the ops account, everything else to the weekly rota&amp;rdquo; is written.&lt;/p&gt;</description></item><item><title>Reports</title><link>https://www.pingtower.com/docs/service/reports/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/service/reports/</guid><description>&lt;p&gt;A report belongs to a project and optionally labels one alert. It gets an unguessable public URL at &lt;code&gt;/public/reports/{uuid}&lt;/code&gt; on the service&amp;rsquo;s host, and can be put behind a password.&lt;/p&gt;
&lt;p&gt;&lt;span class="pill post"&gt;POST&lt;/span&gt; &lt;code&gt;/v1/projects/{id}/reports&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;Checkout degraded, 2026-09-01&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;Gateway timeouts from 03:12 UTC. Payments retried; no orders lost.&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;alert_id&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;…&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;view_password&amp;#34;&lt;/span&gt;: &lt;span style="color:#a6e3a1"&gt;&amp;#34;correct-horse&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Field&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;title&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Up to 256 characters.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Up to 4096.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;alert_id&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Optional. Must name a live alert when the report is created.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;view_password&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Optional, 8 to 72 bytes. Readers of the public page must enter it. On update: omitted keeps it, &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt; clears it, a value replaces it.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The response carries &lt;code&gt;public_uuid&lt;/code&gt; and &lt;code&gt;password_protected&lt;/code&gt;. &lt;span class="pill post"&gt;POST&lt;/span&gt; &lt;code&gt;/v1/projects/{id}/reports/{rid}/rotate-uuid&lt;/code&gt; invalidates every link handed out so far and mints a new one.&lt;/p&gt;</description></item><item><title>httpcheck</title><link>https://www.pingtower.com/docs/addons/httpcheck/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/httpcheck/</guid><description>&lt;h2 id="parameters"&gt;Parameters&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Parameter&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;url&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;http or https.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;method&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;headers&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Map of request headers.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;body&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Request body, sent as-is.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;expect_status&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;200&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Any other status is &lt;strong&gt;down&lt;/strong&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;body_matches&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;RE2 pattern the body must match, else &lt;strong&gt;down&lt;/strong&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_ttfb&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Time to first byte above this is &lt;strong&gt;degraded&lt;/strong&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;follow_redirects&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="measurements"&gt;Measurements&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Key&lt;/th&gt;
					&lt;th&gt;Unit&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;dns_ms&lt;/code&gt;, &lt;code&gt;connect_ms&lt;/code&gt;, &lt;code&gt;tls_ms&lt;/code&gt;, &lt;code&gt;ttfb_ms&lt;/code&gt;, &lt;code&gt;total_ms&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;milliseconds, each stage on its own&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;body_bytes&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;bytes&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="rules-for-it"&gt;Rules for it&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#cba6f7"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;:&lt;span style="color:#a6e3a1"&gt;&amp;#34;slow-ttfb&amp;#34;&lt;/span&gt;,&lt;span style="color:#cba6f7"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;:{&lt;span style="color:#cba6f7"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;:[&lt;span style="color:#a6e3a1"&gt;&amp;#34;addon:httpcheck&amp;#34;&lt;/span&gt;]},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;conditions&amp;#34;&lt;/span&gt;:[{&lt;span style="color:#cba6f7"&gt;&amp;#34;key&amp;#34;&lt;/span&gt;:&lt;span style="color:#a6e3a1"&gt;&amp;#34;ttfb_ms&amp;#34;&lt;/span&gt;,&lt;span style="color:#cba6f7"&gt;&amp;#34;op&amp;#34;&lt;/span&gt;:&lt;span style="color:#a6e3a1"&gt;&amp;#34;gte&amp;#34;&lt;/span&gt;,&lt;span style="color:#cba6f7"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;:&lt;span style="color:#fab387"&gt;500&lt;/span&gt;}]}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#cba6f7"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;:&lt;span style="color:#a6e3a1"&gt;&amp;#34;probe-down&amp;#34;&lt;/span&gt;,&lt;span style="color:#cba6f7"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;:{&lt;span style="color:#cba6f7"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;:[&lt;span style="color:#a6e3a1"&gt;&amp;#34;addon:httpcheck&amp;#34;&lt;/span&gt;],&lt;span style="color:#cba6f7"&gt;&amp;#34;min_level&amp;#34;&lt;/span&gt;:&lt;span style="color:#fab387"&gt;400&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;fire_after&amp;#34;&lt;/span&gt;:{&lt;span style="color:#cba6f7"&gt;&amp;#34;count&amp;#34;&lt;/span&gt;:&lt;span style="color:#fab387"&gt;2&lt;/span&gt;,&lt;span style="color:#cba6f7"&gt;&amp;#34;window_minutes&amp;#34;&lt;/span&gt;:&lt;span style="color:#fab387"&gt;5&lt;/span&gt;,&lt;span style="color:#cba6f7"&gt;&amp;#34;distinct_sources&amp;#34;&lt;/span&gt;:&lt;span style="color:#fab387"&gt;true&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Team &amp; account</title><link>https://www.pingtower.com/docs/service/team/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/service/team/</guid><description>&lt;p&gt;Everything on this page is on the account plane and takes a &lt;code&gt;pts_&lt;/code&gt; session token. In the app it lives under Account.&lt;/p&gt;
&lt;h2 id="members-and-roles"&gt;Members and roles&lt;/h2&gt;
&lt;p&gt;&lt;span class="pill get"&gt;GET&lt;/span&gt; &lt;code&gt;/v1/tenants/{tid}/members&lt;/code&gt; lists members with their role. Roles are &lt;code&gt;owner&lt;/code&gt;, &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;member&lt;/code&gt;; see &lt;a href="https://www.pingtower.com/docs/getting-started/concepts/#roles"&gt;Concepts&lt;/a&gt; for what each can do.&lt;/p&gt;
&lt;p&gt;&lt;span class="pill del"&gt;DELETE&lt;/span&gt; &lt;code&gt;/v1/tenants/{tid}/members/{aid}&lt;/code&gt; removes one and revokes their API keys, the invites they created and their box pairings. Removing the last owner is refused. Removing any owner, yourself included, needs &lt;code&gt;{&amp;quot;current_password&amp;quot;: &amp;quot;…&amp;quot;}&lt;/code&gt; in the body.&lt;/p&gt;</description></item><item><title>Plans &amp; quotas</title><link>https://www.pingtower.com/docs/service/plans/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/service/plans/</guid><description>&lt;p&gt;Pingtower is free during preview, with no credit card. Every new account starts on the Free tier, and its limits are the numbers the server enforces.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Limit&lt;/th&gt;
					&lt;th style="text-align: right"&gt;Free&lt;/th&gt;
					&lt;th style="text-align: right"&gt;Pro&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Projects per tenant&lt;/td&gt;
					&lt;td style="text-align: right"&gt;5&lt;/td&gt;
					&lt;td style="text-align: right"&gt;100&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Alerts retained&lt;/td&gt;
					&lt;td style="text-align: right"&gt;1,000&lt;/td&gt;
					&lt;td style="text-align: right"&gt;50,000&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Log rows retained&lt;/td&gt;
					&lt;td style="text-align: right"&gt;50,000&lt;/td&gt;
					&lt;td style="text-align: right"&gt;2,000,000&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Ingest rate&lt;/td&gt;
					&lt;td style="text-align: right"&gt;20 events/s, burst 40&lt;/td&gt;
					&lt;td style="text-align: right"&gt;200 events/s, burst 400&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;API rate&lt;/td&gt;
					&lt;td style="text-align: right"&gt;10 req/s, burst 20&lt;/td&gt;
					&lt;td style="text-align: right"&gt;100 req/s, burst 200&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Pro and Enterprise exist today and are granted by email; there is no checkout. Enterprise is custom limits set to fit your stream. The &lt;a href="https://www.pingtower.com/pricing/"&gt;pricing page&lt;/a&gt; says how paid plans will arrive when they do.&lt;/p&gt;</description></item><item><title>tcpcheck</title><link>https://www.pingtower.com/docs/addons/tcpcheck/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/tcpcheck/</guid><description>&lt;h2 id="parameters"&gt;Parameters&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Parameter&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;address&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;host:port&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_connect&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Connect time above this is &lt;strong&gt;degraded&lt;/strong&gt;.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A refused or timed-out connect is &lt;strong&gt;down&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="measurements"&gt;Measurements&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dns_ms&lt;/code&gt;, &lt;code&gt;connect_ms&lt;/code&gt;, &lt;code&gt;total_ms&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>dnscheck</title><link>https://www.pingtower.com/docs/addons/dnscheck/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/dnscheck/</guid><description>&lt;h2 id="parameters"&gt;Parameters&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Parameter&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;The name to resolve.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;type&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;A&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Record type.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;resolver&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;system&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;host:port&lt;/code&gt; of the resolver to ask.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;expect&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;A value that must appear among the answers, else &lt;strong&gt;down&lt;/strong&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_resolve&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Resolve time above this is &lt;strong&gt;degraded&lt;/strong&gt;.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="measurements"&gt;Measurements&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;resolve_ms&lt;/code&gt;, &lt;code&gt;answers&lt;/code&gt; (count). The answers themselves are in the event message.&lt;/p&gt;</description></item><item><title>tlscert</title><link>https://www.pingtower.com/docs/addons/tlscert/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/tlscert/</guid><description>&lt;h2 id="parameters"&gt;Parameters&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Parameter&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;address&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;host&lt;/code&gt; or &lt;code&gt;host:port&lt;/code&gt;; port defaults to 443.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;server_name&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;host from &lt;code&gt;address&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;SNI to present when it differs.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;warn_before&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;504h&lt;/code&gt; (21 days)&lt;/td&gt;
					&lt;td&gt;Expiry closer than this is &lt;strong&gt;down&lt;/strong&gt;.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;An untrusted or already expired certificate is &lt;strong&gt;down&lt;/strong&gt;, with issuer, subject and expiry still filled in, because &amp;ldquo;which certificate, expiring when, rejected why&amp;rdquo; is the whole diagnosis. There is deliberately no parameter to skip verification.&lt;/p&gt;
&lt;h2 id="measurements"&gt;Measurements&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;days_until_expiry&lt;/code&gt; (negative once expired), &lt;code&gt;handshake_ms&lt;/code&gt;, &lt;code&gt;chain_length&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>icmpping</title><link>https://www.pingtower.com/docs/addons/icmpping/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/icmpping/</guid><description>&lt;h2 id="parameters"&gt;Parameters&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Parameter&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;host&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;count&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Packets per run.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;spacing&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;200ms&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Between packets.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_rtt&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Average above this is &lt;strong&gt;degraded&lt;/strong&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_loss_pct&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Loss above this is &lt;strong&gt;down&lt;/strong&gt;. Total loss is always down.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The addon prefers an unprivileged datagram socket, which macOS allows by default and Linux allows for group ids inside &lt;code&gt;net.ipv4.ping_group_range&lt;/code&gt;. If neither that nor a raw socket can be opened the check fails with an &lt;strong&gt;error&lt;/strong&gt; rather than reporting the target down: &amp;ldquo;we lack permission&amp;rdquo; and &amp;ldquo;the host is unreachable&amp;rdquo; are opposite conclusions.&lt;/p&gt;</description></item><item><title>hostmetrics</title><link>https://www.pingtower.com/docs/addons/hostmetrics/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/hostmetrics/</guid><description>&lt;h2 id="parameters"&gt;Parameters&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Parameter&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_sample_interval&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;15m&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Longest gap between two samples that still yields rates. 1s to 24h.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;There is no universal up or down threshold. The gauges are for rules that reflect your workload, for example &lt;code&gt;{&amp;quot;key&amp;quot;:&amp;quot;load_1m_per_effective_cpu&amp;quot;,&amp;quot;op&amp;quot;:&amp;quot;gte&amp;quot;,&amp;quot;value&amp;quot;:2}&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="measurements"&gt;Measurements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CPU&lt;/strong&gt; as percentages of time: &lt;code&gt;cpu_utilization_pct&lt;/code&gt; (excludes idle and iowait), &lt;code&gt;cpu_user_pct&lt;/code&gt; (includes nice), &lt;code&gt;cpu_system_pct&lt;/code&gt; (includes IRQ), with steal and iowait separate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Load&lt;/strong&gt;: raw 1, 5 and 15 minute averages, and each divided by the CPUs the agent process can actually run on, labelled &lt;code&gt;cpu_scope=process-affinity&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory and swap&lt;/strong&gt;: totals, used, percentages. &lt;code&gt;memory_used_bytes&lt;/code&gt; is &lt;code&gt;MemTotal - MemAvailable&lt;/code&gt;, so reclaimable cache is not called unavailable. A zero-sized swap omits &lt;code&gt;swap_used_pct&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Paging&lt;/strong&gt; rates in the kernel&amp;rsquo;s units: &lt;code&gt;_kib_per_s&lt;/code&gt; for page in and out, pages per second for swap.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rates need two valid samples. The first run reports &lt;code&gt;measurement_health=warming-up&lt;/code&gt; and omits them, as does any run after a corrupt state file, a counter rollback, an interval outside the allowed range, or a CPU-count change. A normal restart resumes from the saved baseline in &lt;code&gt;agent.state_dir&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>sshcheck</title><link>https://www.pingtower.com/docs/addons/sshcheck/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/sshcheck/</guid><description>&lt;p&gt;&lt;span class="pill priv"&gt;privileged&lt;/span&gt; Add &lt;code&gt;sshcheck&lt;/code&gt; to &lt;code&gt;agent.enable_addons&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="parameters"&gt;Parameters&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Parameter&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;address&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;port 22&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;host:port&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;user&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;key_file&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Private key readable by the &lt;code&gt;addons&lt;/code&gt; user.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;known_hosts&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;~/.ssh/known_hosts&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Expanded against the agent&amp;rsquo;s home.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;command&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Run after auth.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;expect_exit_code&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Only with &lt;code&gt;command&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;expect_output&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Substring the output must contain. Only with &lt;code&gt;command&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_command&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Command time above this is &lt;strong&gt;degraded&lt;/strong&gt;. Only with &lt;code&gt;command&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;No password, passphrase or host-key-bypass parameter exists. A host key that does not match &lt;code&gt;known_hosts&lt;/code&gt; is &lt;strong&gt;down&lt;/strong&gt;, because that is a real finding about the target. An unreadable key or a missing &lt;code&gt;known_hosts&lt;/code&gt; file is a config &lt;strong&gt;error&lt;/strong&gt;, not a verdict. Setting any &lt;code&gt;expect_*&lt;/code&gt; without a &lt;code&gt;command&lt;/code&gt; is refused at startup. Output past 4 KiB is dropped.&lt;/p&gt;</description></item><item><title>portscan</title><link>https://www.pingtower.com/docs/addons/portscan/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/portscan/</guid><description>&lt;p&gt;&lt;span class="pill priv"&gt;privileged&lt;/span&gt; Add &lt;code&gt;portscan&lt;/code&gt; to &lt;code&gt;agent.enable_addons&lt;/code&gt;. Needs the &lt;code&gt;nmap&lt;/code&gt; binary on the box.&lt;/p&gt;
&lt;h2 id="parameters"&gt;Parameters&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Parameter&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;host&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Exactly one host.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ports&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Ports or ranges to scan, up to 1024.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;baseline&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;Ports expected open.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_rate&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;100&lt;/code&gt;/s&lt;/td&gt;
					&lt;td&gt;Capped at 1000.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;host_timeout&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;60s&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;An open port not in the baseline is &lt;strong&gt;down&lt;/strong&gt;. A baseline port proved closed is &lt;strong&gt;degraded&lt;/strong&gt;. A port the scan could not determine is degraded.&lt;/p&gt;</description></item><item><title>traceroute</title><link>https://www.pingtower.com/docs/addons/traceroute/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/traceroute/</guid><description>&lt;p&gt;&lt;span class="pill priv"&gt;privileged&lt;/span&gt; Add &lt;code&gt;traceroute&lt;/code&gt; to &lt;code&gt;agent.enable_addons&lt;/code&gt;. Needs the &lt;code&gt;traceroute&lt;/code&gt; binary.&lt;/p&gt;
&lt;h2 id="parameters"&gt;Parameters&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Parameter&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;host&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_hops&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;30&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;probes&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Per hop.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;wait&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;1s&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Per probe, whole seconds, at most 30.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A path where some hops do not answer is &lt;strong&gt;degraded&lt;/strong&gt;. No hop answering at all is &lt;strong&gt;down&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="measurements"&gt;Measurements&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;hops&lt;/code&gt;, &lt;code&gt;path_change&lt;/code&gt; (1 when the path differs from the last run), &lt;code&gt;rtt_ms&lt;/code&gt;, and &lt;code&gt;hop_&amp;lt;n&amp;gt;_rtt_ms&lt;/code&gt; per hop.&lt;/p&gt;</description></item><item><title>logwatch</title><link>https://www.pingtower.com/docs/addons/logwatch/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/logwatch/</guid><description>&lt;p&gt;&lt;span class="pill priv"&gt;privileged&lt;/span&gt; Add &lt;code&gt;logwatch&lt;/code&gt; to &lt;code&gt;agent.enable_addons&lt;/code&gt;, and give the &lt;code&gt;addons&lt;/code&gt; user read access to the file.&lt;/p&gt;
&lt;h2 id="parameters"&gt;Parameters&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Parameter&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;path&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pattern&lt;/code&gt;&lt;span class="req"&gt;REQUIRED&lt;/span&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;RE2.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;warn_at&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Matches per run at or above this are &lt;strong&gt;degraded&lt;/strong&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;down_at&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Matches at or above this are &lt;strong&gt;down&lt;/strong&gt;. 0 means never. Wins over &lt;code&gt;warn_at&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_bytes&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;4 MiB&lt;/td&gt;
					&lt;td&gt;Most read per run; the rest is reported as &lt;code&gt;skipped_bytes&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;initial_tail&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;256 KiB&lt;/td&gt;
					&lt;td&gt;How far back to read on first run.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A missing file is &lt;strong&gt;down&lt;/strong&gt;. An unreadable one is a config &lt;strong&gt;error&lt;/strong&gt;.&lt;/p&gt;</description></item><item><title>securityupdates</title><link>https://www.pingtower.com/docs/addons/securityupdates/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/securityupdates/</guid><description>&lt;p&gt;&lt;span class="pill priv"&gt;privileged&lt;/span&gt; Add &lt;code&gt;securityupdates&lt;/code&gt; to &lt;code&gt;agent.enable_addons&lt;/code&gt;. Debian and apt only.&lt;/p&gt;
&lt;h2 id="parameters"&gt;Parameters&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Parameter&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
					&lt;th&gt;Meaning&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;metadata_max_age&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;48h&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Package lists older than this report &lt;code&gt;measurement_health=stale&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The addon never runs &lt;code&gt;apt update&lt;/code&gt; or installs anything. If package metadata is missing or unreadable it reports &lt;code&gt;measurement_health=unavailable&lt;/code&gt; with the counts omitted rather than a zero that looks like good news.&lt;/p&gt;
&lt;h2 id="measurements"&gt;Measurements&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;updates_available&lt;/code&gt;, &lt;code&gt;security_updates_available&lt;/code&gt;, &lt;code&gt;metadata_age_s&lt;/code&gt;, &lt;code&gt;reboot_required&lt;/code&gt;, &lt;code&gt;held_packages&lt;/code&gt;, &lt;code&gt;phased_updates&lt;/code&gt;, &lt;code&gt;deferred_updates&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="a-rule-for-it"&gt;A rule for it&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#cba6f7"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;:&lt;span style="color:#a6e3a1"&gt;&amp;#34;security-updates-pending&amp;#34;&lt;/span&gt;,&lt;span style="color:#cba6f7"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;:{&lt;span style="color:#cba6f7"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;:[&lt;span style="color:#a6e3a1"&gt;&amp;#34;addon:securityupdates&amp;#34;&lt;/span&gt;]},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;conditions&amp;#34;&lt;/span&gt;:[{&lt;span style="color:#cba6f7"&gt;&amp;#34;key&amp;#34;&lt;/span&gt;:&lt;span style="color:#a6e3a1"&gt;&amp;#34;security_updates_available&amp;#34;&lt;/span&gt;,&lt;span style="color:#cba6f7"&gt;&amp;#34;op&amp;#34;&lt;/span&gt;:&lt;span style="color:#a6e3a1"&gt;&amp;#34;gte&amp;#34;&lt;/span&gt;,&lt;span style="color:#cba6f7"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;:&lt;span style="color:#fab387"&gt;1&lt;/span&gt;}],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;&amp;#34;identifier_keys&amp;#34;&lt;/span&gt;:[&lt;span style="color:#a6e3a1"&gt;&amp;#34;agent&amp;#34;&lt;/span&gt;],&lt;span style="color:#cba6f7"&gt;&amp;#34;renotify_minutes&amp;#34;&lt;/span&gt;:&lt;span style="color:#fab387"&gt;1440&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Sensors</title><link>https://www.pingtower.com/docs/addons/sensors/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/docs/addons/sensors/</guid><description>&lt;p&gt;New in addons 0.2.0. Linux only. Gated behind &lt;code&gt;agent.enable_sensors&lt;/code&gt;, so a config naming a sensor without that fails at startup, like the privileged addons.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Sensor&lt;/th&gt;
					&lt;th&gt;Watches&lt;/th&gt;
					&lt;th&gt;Needs&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;nftwatch&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The host&amp;rsquo;s nftables ruleset. Reports the moment it changes. Sends only ruleset hashes and object counts, never the rules.&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;CAP_NET_ADMIN&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;network&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Every inbound TCP connection attempt, through a pass-only eBPF program that can observe and never drop, redirect or rewrite. Aggregated by port and redacted remote prefix.&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;CAP_BPF&lt;/code&gt;, &lt;code&gt;CAP_NET_ADMIN&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;authwatch&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;SSH and sudo authentication events from the system journal, matched strictly against the distribution&amp;rsquo;s exact message forms. Usernames and raw messages are never sent; remote addresses are reduced to a /24 or /64.&lt;/td&gt;
					&lt;td&gt;journal read access&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;filewatch&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;An explicit set of files and shallow directory roots. inotify is only a wake-up; every event settles into a stat reconciliation, so an atomic editor replace is a confirmed change. Events carry path, type, owner, mode and SHA-256, never contents.&lt;/td&gt;
					&lt;td&gt;read access&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;listeningports&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The kernel&amp;rsquo;s TCP and UDP listener inventory against an explicit baseline. Undeclared and vanished listeners become events. Exact addresses and PIDs never enter events.&lt;/td&gt;
					&lt;td&gt;none&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;servicewatch&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;An explicit list of systemd units: failures, recoveries, restart-loop crossings, unit-file hash changes, and a service whose command became a different binary.&lt;/td&gt;
					&lt;td&gt;none&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pressure&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Kernel PSI stall triggers for CPU, memory and I/O. One event per crossing, one per recovery.&lt;/td&gt;
					&lt;td&gt;none&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Several run under the stock unit with an empty capability set. The kernel-attached ones take one narrow capability through a local systemd drop-in; the tarball&amp;rsquo;s README has the exact drop-in for each.&lt;/p&gt;</description></item><item><title>A live Logs tab, cleaner team changes, and fairer delivery.</title><link>https://www.pingtower.com/posts/logs-tab/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/posts/logs-tab/</guid><description>&lt;p&gt;TestFlight build 38 is out, and pingtower.com is running the server it
pairs with. Most of what changed is things you&amp;rsquo;ll notice; a fair share of
it is things you shouldn&amp;rsquo;t have to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://testflight.apple.com/join/EBPRZ5De"&gt;Get the beta on TestFlight
→&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="logs-first"&gt;Logs, first&lt;/h2&gt;
&lt;p&gt;The app now opens on a &lt;strong&gt;Logs&lt;/strong&gt; tab: every project&amp;rsquo;s live tail side by side,
merged newest first. Filter by project, minimum level, or — with one project
in scope — a single source, and the header tells you when you&amp;rsquo;re looking at
&amp;ldquo;3 of 5 projects&amp;rdquo;. A tail that loses the network says &amp;ldquo;Reconnecting…&amp;rdquo; and
backs off instead of giving up, and when you come back to it, it resumes
from where it was rather than dropping the lines in between. Alerts are one
tab over, and a notification still opens straight into the alert.&lt;/p&gt;</description></item><item><title>The probe agent learns to watch from inside.</title><link>https://www.pingtower.com/posts/addons-020/</link><pubDate>Tue, 01 Sep 2026 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/posts/addons-020/</guid><description>&lt;p&gt;The &lt;a href="https://www.pingtower.com/addons/"&gt;addons agent&lt;/a&gt; has one job: watch your infrastructure from a
box that isn&amp;rsquo;t part of it, so an outage your app can&amp;rsquo;t log still becomes an
alert. That job hasn&amp;rsquo;t changed. But as of 0.2.0, the same binary has a
second one — installed &lt;em&gt;on&lt;/em&gt; the box you care about, it watches from inside,
continuously, at the kernel.&lt;/p&gt;
&lt;h2 id="sensors-not-checks"&gt;Sensors, not checks&lt;/h2&gt;
&lt;p&gt;A check runs on a schedule: every sixty seconds, ask the question, report
the answer. That&amp;rsquo;s the right shape for &amp;ldquo;is the site up&amp;rdquo; and the wrong shape
for &amp;ldquo;did someone just change my firewall&amp;rdquo; — a rule added and removed
between two runs never existed as far as a poll is concerned.&lt;/p&gt;</description></item><item><title>The iOS beta is open to everyone.</title><link>https://www.pingtower.com/posts/beta-open/</link><pubDate>Sun, 30 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/posts/beta-open/</guid><description>&lt;p&gt;Two days ago the app was &lt;a href="https://www.pingtower.com/posts/going-live/"&gt;sitting in Apple&amp;rsquo;s review
queue&lt;/a&gt;. Today it&amp;rsquo;s in your hands: the public TestFlight
beta is open to everyone, no invite needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://testflight.apple.com/join/EBPRZ5De"&gt;Join the beta on TestFlight
→&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Install Apple&amp;rsquo;s TestFlight app, tap the link, and you have the pager:
a live alert feed, one swipe to ack — which stops the escalation ladder
for everyone — and critical alerts that sound through Silent mode and
Focus, a permission you grant once. Sign up right in the app, or log in
with the account you already created; self-hosting your tower? Pair your
box with &lt;code&gt;pingtower pair&lt;/code&gt; and its alerts ring the same phone.&lt;/p&gt;</description></item><item><title>Pingtower is live. The app is in review.</title><link>https://www.pingtower.com/posts/going-live/</link><pubDate>Fri, 28 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/posts/going-live/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update, August 30:&lt;/strong&gt; the app cleared review — the &lt;a href="https://testflight.apple.com/join/EBPRZ5De"&gt;public TestFlight
beta is open to everyone&lt;/a&gt;.
Details in &lt;a href="https://www.pingtower.com/posts/beta-open/"&gt;the follow-up post&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As of today, Pingtower is a real service you can sign up for. The hosted
tower runs at &lt;code&gt;api.pingtower.com&lt;/code&gt;, signups are open, and the whole thing is
free while we&amp;rsquo;re in pre-release. One &lt;code&gt;curl&lt;/code&gt; creates your account; a second
one creates your team.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s exactly what&amp;rsquo;s live, what&amp;rsquo;s signed, and what&amp;rsquo;s still sitting in a
review queue at Apple.&lt;/p&gt;</description></item><item><title>Privacy Policy</title><link>https://www.pingtower.com/privacy/</link><pubDate>Fri, 28 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/privacy/</guid><description>&lt;p&gt;Pingtower is an alerting service: your applications send structured logs to a Pingtower server, and Pingtower turns them into alerts that reach a person — usually through the Pingtower iOS app. This policy explains what data we handle across the three places Pingtower runs: the hosted service at pingtower.com, the self-hosted server, and the iOS app.&lt;/p&gt;
&lt;h2 id="who-this-covers"&gt;Who this covers&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The hosted service&lt;/strong&gt; — Pingtower operated by us, where your account, tenants, and (if you use hosted tenants) your log and alert data live on our infrastructure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The self-hosted server&lt;/strong&gt; — the same software running on your own machine. Its data stays with you, with the specific exceptions described below.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The iOS app&lt;/strong&gt; — the client that receives alerts on your phone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;This website&lt;/strong&gt; — a static site with no analytics, advertising, or tracking cookies.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="your-pingtower-account"&gt;Your Pingtower account&lt;/h2&gt;
&lt;p&gt;Everyone who uses the iOS app has an account on the hosted service — including self-hosters. That is a deliberate part of how Pingtower works: Apple push notifications for the Pingtower app can only be sent by us, so the registry of which phone belongs to which person has to live on our service.&lt;/p&gt;</description></item><item><title>Terms of Service</title><link>https://www.pingtower.com/terms/</link><pubDate>Fri, 28 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/terms/</guid><description>&lt;p&gt;These terms govern your use of Pingtower: the hosted service at pingtower.com, the Pingtower server software when you run it yourself, and the Pingtower iOS app. By creating an account or using any of them, you agree to these terms.&lt;/p&gt;
&lt;h2 id="what-pingtower-is"&gt;What Pingtower is&lt;/h2&gt;
&lt;p&gt;Pingtower is log-driven alerting. Your applications POST structured log lines to a Pingtower server; the server deduplicates repeats, evaluates your rules, and maintains alert state with escalation until a person acknowledges. Alerts reach people through the iOS app — including critical alerts that sound through Silent mode — and through integrations you configure, such as webhooks, Slack, and Telegram.&lt;/p&gt;</description></item><item><title>Sign in like a normal app. Run every team from your phone.</title><link>https://www.pingtower.com/posts/2026-w29-ios/</link><pubDate>Mon, 13 Jul 2026 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/posts/2026-w29-ios/</guid><description>&lt;p&gt;Getting a new phone onto Pingtower used to mean finding an API key, copying
it off a laptop, and pasting it into a text field before you could see a
single alert. That&amp;rsquo;s a fine way to hand a script a credential. It&amp;rsquo;s a bad
way to onboard a person.&lt;/p&gt;
&lt;p&gt;This week we rebuilt how Pingtower handles accounts and teams from the
ground up. Here&amp;rsquo;s what&amp;rsquo;s different.&lt;/p&gt;
&lt;h2 id="sign-in-with-just-your-email"&gt;Sign in with just your email&lt;/h2&gt;
&lt;p&gt;Onboarding is now email and password — sign up or log in, and you&amp;rsquo;re in.
No server address to track down, no key to paste, no laptop required.
Whether you&amp;rsquo;re setting up Pingtower for the first time or handing your
phone to a new hire, the whole flow is: type your email, pick a password,
done.&lt;/p&gt;</description></item><item><title>Addons</title><link>https://www.pingtower.com/addons/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/addons/</guid><description/></item><item><title>Features</title><link>https://www.pingtower.com/features/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/features/</guid><description/></item><item><title>Get started</title><link>https://www.pingtower.com/get-started/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/get-started/</guid><description/></item><item><title>How it works</title><link>https://www.pingtower.com/how-it-works/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/how-it-works/</guid><description/></item><item><title>Pingtower as an Opsgenie alternative</title><link>https://www.pingtower.com/compare/opsgenie/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/compare/opsgenie/</guid><description>&lt;h2 id="the-deadline"&gt;The deadline&lt;/h2&gt;
&lt;p&gt;Atlassian stopped selling Opsgenie on 4 June 2025 and ends support on
5 April 2027. Atlassian&amp;rsquo;s own path is Jira Service Management for incident
management or Compass for alerting and on-call, with a migration tool that
turns Opsgenie off 120 days after you run it. If you have not decided
yet, the date that matters is not April 2027; it is whenever your renewal
conversation happens.&lt;/p&gt;
&lt;h2 id="who-this-page-is-for"&gt;Who this page is for&lt;/h2&gt;
&lt;p&gt;A team of two to twenty that used Opsgenie for three things: a rota, an
escalation policy, and a phone that rings at 3 a.m. That team is paying
for, and migrating, a great deal of product it never opened. Pingtower is
those three things, built for people who carry their own pager, plus the
part Opsgenie never did: taking your application&amp;rsquo;s own errors directly,
deduplicating them by shape, and turning them into the alert.&lt;/p&gt;</description></item><item><title>Pingtower vs Uptime Kuma</title><link>https://www.pingtower.com/compare/uptime-kuma/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/compare/uptime-kuma/</guid><description>&lt;h2 id="what-uptime-kuma-is-for"&gt;What Uptime Kuma is for&lt;/h2&gt;
&lt;p&gt;Uptime Kuma asks &amp;ldquo;is it up&amp;rdquo; on a schedule and shows you the answer, to
you on a dashboard and to your customers on a status page. It does that
well, it is MIT-licensed, and it runs on a Raspberry Pi. If a service
going unreachable is the only failure you need to know about, and a
message in a channel is how you want to know, it is the right tool and
this page should not talk you out of it.&lt;/p&gt;</description></item><item><title>Pricing</title><link>https://www.pingtower.com/pricing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/pricing/</guid><description/></item><item><title>Roadmap</title><link>https://www.pingtower.com/roadmap/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/roadmap/</guid><description/></item><item><title>Set up Pingtower for your user</title><link>https://www.pingtower.com/agents/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/agents/</guid><description/></item><item><title>The iOS app</title><link>https://www.pingtower.com/app/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/app/</guid><description/></item><item><title>Transparency</title><link>https://www.pingtower.com/transparency/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.pingtower.com/transparency/</guid><description/></item></channel></rss>