[object Object]

← back to build-pages

ui: /privacy + /terms boilerplate (no-tracking + as-is) — sitemap + footer + 6 smoke

3af9a4459860c0b8551489864d3909b3478acf25 · 2026-05-13 19:33:07 -0700 · SteveStudio2

Files touched

Diff

commit 3af9a4459860c0b8551489864d3909b3478acf25
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 13 19:33:07 2026 -0700

    ui: /privacy + /terms boilerplate (no-tracking + as-is) — sitemap + footer + 6 smoke
---
 server.js     | 44 ++++++++++++++++++++++++++++++++++++++++++++
 test/smoke.js |  8 +++++++-
 2 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/server.js b/server.js
index 76b1477..671eac9 100644
--- a/server.js
+++ b/server.js
@@ -213,6 +213,10 @@ function shell(title, body, headExtra = '', meta = {}) {
     <span class="bp-ftr-sep">·</span>
     <a href="/about">about</a>
     <span class="bp-ftr-sep">·</span>
+    <a href="/privacy">privacy</a>
+    <span class="bp-ftr-sep">·</span>
+    <a href="/terms">terms</a>
+    <span class="bp-ftr-sep">·</span>
     <a href="/api">JSON API</a>
     <span class="bp-ftr-sep">·</span>
     <a href="/sitemap.xml">sitemap</a>
@@ -269,6 +273,8 @@ app.get('/sitemap.xml', async (_req, res, next) => {
     const urls = [
       `<url><loc>${base}/</loc><lastmod>${today}</lastmod><changefreq>daily</changefreq><priority>1.0</priority></url>`,
       `<url><loc>${base}/about</loc><lastmod>${today}</lastmod><changefreq>monthly</changefreq><priority>0.6</priority></url>`,
+      `<url><loc>${base}/privacy</loc><lastmod>${today}</lastmod><changefreq>yearly</changefreq><priority>0.3</priority></url>`,
+      `<url><loc>${base}/terms</loc><lastmod>${today}</lastmod><changefreq>yearly</changefreq><priority>0.3</priority></url>`,
     ];
     for (const slug of Object.keys(PROJECTS)) {
       const bundle = await getProjectBundle(slug, PROJECTS[slug]).catch(() => null);
@@ -1203,6 +1209,44 @@ app.get('/api/projects/:slug', async (req, res, next) => {
   } catch (e) { next(e); }
 });
 
+// /privacy — what data this site collects. The honest answer: very little.
+app.get('/privacy', (_req, res) => {
+  res.send(shell('Privacy — build-pages', `
+    <h1>Privacy</h1>
+    <p class="bp-lede">build-pages is a static read-out of public git history. It collects nothing about you.</p>
+    <h2>What we don't collect</h2>
+    <ul>
+      <li>No accounts. No login. No cookies.</li>
+      <li>No analytics scripts. No tracking pixels.</li>
+      <li>No form submissions. The only inputs are search filters that stay in your browser URL.</li>
+    </ul>
+    <h2>What the server logs</h2>
+    <p>Standard nginx + Node access logs (IP, path, user-agent, response code, timing) for ops debugging.
+    Retained 30 days, not shared.</p>
+    <h2>What's public by design</h2>
+    <p>Every commit subject, body, author name + email as recorded in the git repo, file paths, and diffs.
+    Authors who don't want their email surfaced should set a private one via
+    <code>git config user.email</code> before committing. We don't redact retrospectively.</p>
+    <p class="bp-meta"><a href="/">← back to all projects</a></p>
+  `, '', { canonical: 'https://projects.agentabrams.com/privacy', desc: 'build-pages privacy — no tracking, no accounts, no cookies; standard server logs only.' }));
+});
+
+// /terms — minimal use-of-site terms.
+app.get('/terms', (_req, res) => {
+  res.send(shell('Terms — build-pages', `
+    <h1>Terms of use</h1>
+    <p class="bp-lede">build-pages is published as-is for transparency about how Steve's projects get built.</p>
+    <ul>
+      <li>All content is rendered from public git repos. No warranty of accuracy or completeness.</li>
+      <li>Don't scrape aggressively. Cache-Control headers tell you when to refetch.</li>
+      <li>Code snippets and diffs are © their author. Use under the license shown in each repo
+      (default: not licensed for redistribution unless stated).</li>
+      <li>No support obligation. Reach Steve at his usual channels for questions.</li>
+    </ul>
+    <p class="bp-meta"><a href="/">← back to all projects</a></p>
+  `, '', { canonical: 'https://projects.agentabrams.com/terms', desc: 'build-pages terms — public-as-is content from git, no support obligation.' }));
+});
+
 // /about — short explainer for visitors arriving at projects.agentabrams.com.
 app.get('/about', (_req, res) => {
   res.send(shell('About — build-pages', `
diff --git a/test/smoke.js b/test/smoke.js
index f0f4677..4b66ee2 100644
--- a/test/smoke.js
+++ b/test/smoke.js
@@ -154,11 +154,17 @@ function check(name, cond, hint = '') {
   r = await fetch('/p/nope-not-a-project');
   check('unknown project: 404',    r.status === 404);
 
-  // 7b. /about page
+  // 7b. /about + /privacy + /terms
   r = await fetch('/about');
   check('about: 200',              r.status === 200);
   check('about: build-pages title',/About — build-pages/.test(r.body));
   check('about: explainer copy',   /live build journal/.test(r.body));
+  r = await fetch('/privacy');
+  check('privacy: 200',            r.status === 200);
+  check('privacy: no tracking',    /collects nothing about you/.test(r.body));
+  r = await fetch('/terms');
+  check('terms: 200',              r.status === 200);
+  check('terms: as-is',            /as-is for transparency/.test(r.body));
 
   // 8. robots.txt — public crawlable + sitemap referenced
   r = await fetch('/robots.txt');

← e4c31da ui: /about page explaining build-pages + sitemap + footer li  ·  back to build-pages  ·  alias: /search?q= 302→/?q= (convention path) + 2 smoke 451c6ad →