Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ RUN for f in /api/migrations-pg/*.sql; do \
head -1 "$f" | grep -q '^-- TronBrowser Postgres schema:' \
|| { echo "not a Postgres migration: $f" >&2; exit 1; }; \
done
COPY scripts/pit-footer.mjs /pit-footer.mjs
COPY start.sh /start.sh
RUN chmod +x /start.sh
CMD ["/start.sh"]
3 changes: 2 additions & 1 deletion apps/extensions/public/extension.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@
</main>

<footer><div class="wrap">
TronBrowser Store · <a href="/">tronbrowser.dev</a> · <a href="/store/">Browse</a> · <a href="/store/install-guide.html">Install guide</a> · <a href="/store/submit.html">Publish</a> · <a href="https://github.com/profullstack/tronbrowser.dev">GitHub</a> · every listing carries a vu1nz.com scan — report abuse from any listing.
TronBrowser Store · <a href="/">tronbrowser.dev</a> · <a href="/store/">Browse</a> · <a href="/store/install-guide.html">Install guide</a> · <a href="/store/submit.html">Publish</a> · <a href="https://github.com/profullstack/tronbrowser.dev">GitHub</a>
</div></footer>

<script src="/store/store.js"></script>
<!--pit-footer--><!--/pit-footer-->
</body>
</html>
3 changes: 2 additions & 1 deletion apps/extensions/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ <h1>Browser extensions, without the gatekeepers.</h1>
</main>

<footer><div class="wrap">
TronBrowser Store · <a href="/">tronbrowser.dev</a> · <a href="/store/">Browse</a> · <a href="/store/install-guide.html">Install guide</a> · <a href="/store/submit.html">Publish</a> · <a href="https://github.com/profullstack/tronbrowser.dev">GitHub</a> · every listing carries a vu1nz.com scan — report abuse from any listing.
TronBrowser Store · <a href="/">tronbrowser.dev</a> · <a href="/store/">Browse</a> · <a href="/store/install-guide.html">Install guide</a> · <a href="/store/submit.html">Publish</a> · <a href="https://github.com/profullstack/tronbrowser.dev">GitHub</a>
</div></footer>

<script src="/store/store.js"></script>
<!--pit-footer--><!--/pit-footer-->
</body>
</html>
3 changes: 2 additions & 1 deletion apps/extensions/public/install-guide.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,10 @@ <h2>Publishing via a PR (alternative to the upload form)</h2>
</main>

<footer><div class="wrap">
TronBrowser Store · <a href="/">tronbrowser.dev</a> · <a href="/store/">Browse</a> · <a href="/store/install-guide.html">Install guide</a> · <a href="/store/submit.html">Publish</a> · <a href="https://github.com/profullstack/tronbrowser.dev">GitHub</a> · every listing carries a vu1nz.com scan — report abuse from any listing.
TronBrowser Store · <a href="/">tronbrowser.dev</a> · <a href="/store/">Browse</a> · <a href="/store/install-guide.html">Install guide</a> · <a href="/store/submit.html">Publish</a> · <a href="https://github.com/profullstack/tronbrowser.dev">GitHub</a>
</div></footer>

<script src="/store/store.js"></script>
<!--pit-footer--><!--/pit-footer-->
</body>
</html>
3 changes: 2 additions & 1 deletion apps/extensions/public/submit.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ <h3>5 · Pay the $1 listing fee</h3>
</main>

<footer><div class="wrap">
TronBrowser Store · <a href="/">tronbrowser.dev</a> · <a href="/store/">Browse</a> · <a href="/store/install-guide.html">Install guide</a> · <a href="/store/submit.html">Publish</a> · <a href="https://github.com/profullstack/tronbrowser.dev">GitHub</a> · every listing carries a vu1nz.com scan — report abuse from any listing.
TronBrowser Store · <a href="/">tronbrowser.dev</a> · <a href="/store/">Browse</a> · <a href="/store/install-guide.html">Install guide</a> · <a href="/store/submit.html">Publish</a> · <a href="https://github.com/profullstack/tronbrowser.dev">GitHub</a>
</div></footer>

<script src="/store/store.js"></script>
<!--pit-footer--><!--/pit-footer-->
</body>
</html>
1 change: 1 addition & 0 deletions apps/web/public/404.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
<body>
<h1>4<span class="accent">0</span>4</h1>
<p>That page isn't here. Try the <a href="/">homepage</a>.</p>
<!--pit-footer--><!--/pit-footer-->
</body>
</html>
1 change: 1 addition & 0 deletions apps/web/public/dns.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,6 @@ <h2>How to apply</h2>
</ol>
</div>
<script src="/dns.js"></script>
<!--pit-footer--><!--/pit-footer-->
</body>
</html>
1 change: 1 addition & 0 deletions apps/web/public/ext-callback.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ <h1 id="msg">Connecting your account…</h1>
surface — the token never reaches the server (it's in the fragment). If you
landed here outside TronBrowser, nothing happens.
-->
<!--pit-footer--><!--/pit-footer-->
</body></html>
29 changes: 14 additions & 15 deletions apps/web/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@
.faq h3 { color: var(--cyan); font-size: 16px; margin: 22px 0 6px; }
.faq p { margin: 0; color: var(--fg); }
.site-foot { text-align: center; margin-top: 64px; color: var(--muted); }
.foot-links { font-size: 14px; }
.foot-links a { color: var(--muted); }
.onion { margin-top: 12px; font-size: 13px; }
.onion a { color: var(--muted); word-break: break-all; }
.onion .dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%;
Expand All @@ -184,6 +186,16 @@ <h1 class="headline">TronBrowser — privacy-first, AI-native browser</h1>
</header>

<main>
<!-- CrawlProof ad: one script-free cross-origin frame (/api/ads/frame), not ad.js.
ad.js fills a srcdoc frame that inherits this page's CSP, and img-src 'self' would
blank the creative. text_link is the fluid format (40px at any width), so one unit
fits every viewport; it carries its own "Sponsored" mark. CSP cost: frame-src. -->
<aside class="cp-ad" aria-label="Advertisement" style="max-width:72rem;margin:0 auto 2rem;padding:0 1rem">
<iframe src="https://crawlproof.com/api/ads/frame?slot=42d8cb56-9003-4a80-ab9b-17704a86714c&amp;format=text_link"
title="Advertisement" height="40" loading="lazy" scrolling="no"
style="display:block;width:100%;height:40px;border:0"
sandbox="allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation"></iframe>
</aside>
<p class="lede">
TronBrowser is an open-source, privacy-first, AI-native web browser built on
<strong>Ungoogled Chromium</strong>. No telemetry, no ads, no sponsored tabs —
Expand Down Expand Up @@ -248,29 +260,16 @@ <h3>How do I install it?</h3>
then <code>tron &lt;url&gt;</code>. Windows builds are on the
<a href="https://github.com/profullstack/tronbrowser.dev/releases">releases page</a>.</p>
</div>
<!-- CrawlProof ad: one script-free cross-origin frame (/api/ads/frame), not ad.js.
ad.js fills a srcdoc frame that inherits this page's CSP, and img-src 'self' would
blank the creative. text_link is the fluid format (40px at any width), so one unit
fits every viewport; it carries its own "Sponsored" mark. CSP cost: frame-src. -->
<aside class="cp-ad" aria-label="Advertisement" style="max-width:72rem;margin:2rem auto 0;padding:0 1rem">
<iframe src="https://crawlproof.com/api/ads/frame?slot=42d8cb56-9003-4a80-ab9b-17704a86714c&amp;format=text_link"
title="Advertisement" height="40" loading="lazy" scrolling="no"
style="display:block;width:100%;height:40px;border:0"
sandbox="allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation"></iframe>
</aside>
</main>

<div class="site-foot">
<!-- @profullstack/footer: server-rendered copy for crawlers and the ring verifier; embed.js swaps in the @latest template. -->
<footer data-profullstack-footer data-site="https://tronbrowser.dev/" data-since="2026" data-tagline="MIT License" data-links='[{"label":"Store","href":"/store/"},{"label":"Sign in","href":"/login"},{"label":"Privacy","href":"/privacy"},{"label":"GitHub","href":"https://github.com/profullstack/tronbrowser.dev"},{"label":"PRD","href":"https://github.com/profullstack/tronbrowser.dev/blob/main/docs/tronbrowser-prd.md"},{"label":"Releases","href":"https://github.com/profullstack/tronbrowser.dev/releases"}]'>
<footer class="pfs-footer" data-pfs-footer="0.2.1"><style>.pfs-footer{--pfs-footer-muted:color-mix(in srgb,currentColor 62%,transparent);--pfs-footer-gap:.5rem 1.25rem;--pfs-footer-size:.875rem;border-top:1px solid color-mix(in srgb,currentColor 14%,transparent);font-size:var(--pfs-footer-size);line-height:1.5;color:inherit}.pfs-footer__inner{max-width:72rem;margin:0 auto;padding:1.5rem 1rem;display:flex;flex-direction:column;align-items:center;gap:.75rem;text-align:center}.pfs-footer a{color:var(--pfs-footer-muted);text-decoration:none}.pfs-footer a:hover,.pfs-footer a:focus-visible{color:inherit;text-decoration:underline}.pfs-footer__links{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--pfs-footer-gap)}.pfs-footer__ring{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;gap:.6rem}.pfs-footer__icon{display:inline-block;width:1em;height:1em;vertical-align:-.125em;background-color:currentColor;-webkit-mask:var(--pfs-footer-glyph) center/contain no-repeat;mask:var(--pfs-footer-glyph) center/contain no-repeat}.pfs-footer__icon--random{--pfs-footer-glyph:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Crect%20x%3D'3'%20y%3D'3'%20width%3D'18'%20height%3D'18'%20rx%3D'3'%2F%3E%3Ccircle%20cx%3D'8'%20cy%3D'8'%20r%3D'1'%2F%3E%3Ccircle%20cx%3D'16'%20cy%3D'8'%20r%3D'1'%2F%3E%3Ccircle%20cx%3D'12'%20cy%3D'12'%20r%3D'1'%2F%3E%3Ccircle%20cx%3D'8'%20cy%3D'16'%20r%3D'1'%2F%3E%3Ccircle%20cx%3D'16'%20cy%3D'16'%20r%3D'1'%2F%3E%3C%2Fsvg%3E")}.pfs-footer__icon--vote{--pfs-footer-glyph:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'2'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M12%204%2021%2019H3z'%2F%3E%3C%2Fsvg%3E")}.pfs-footer__copy{margin:0;color:var(--pfs-footer-muted)}.pfs-footer__copy a{color:inherit}</style><div class="pfs-footer__inner"><nav class="pfs-footer__links" aria-label="Footer"><a href="/store/">Store</a><a href="/login">Sign in</a><a href="/privacy">Privacy</a><a href="https://github.com/profullstack/tronbrowser.dev">GitHub</a><a href="https://github.com/profullstack/tronbrowser.dev/blob/main/docs/tronbrowser-prd.md">PRD</a><a href="https://github.com/profullstack/tronbrowser.dev/releases">Releases</a></nav><nav class="webring pfs-footer__ring" aria-label="Profullstack webring"><a href="https://rssamplifier.com/ring/profullstack/previous?from=https%3A%2F%2Ftronbrowser.dev%2F" rel="prev" title="Previous site">&lt;&lt;</a><a href="https://rssamplifier.com/ring/profullstack">Profullstack</a><a href="https://rssamplifier.com/ring/profullstack/next?from=https%3A%2F%2Ftronbrowser.dev%2F" rel="next" title="Next site">&gt;&gt;</a></nav><p class="pfs-footer__copy">&copy; 2026 <a href="https://profullstack.com/">Profullstack, Inc.</a> &middot; MIT License</p></div></footer>
</footer>
<footer class="foot-links">&copy; 2026 TronBrowser &middot; MIT License &middot; <a href="/store/">Store</a> &middot; <a href="/login">Sign in</a> &middot; <a href="/privacy">Privacy</a> &middot; <a href="https://github.com/profullstack/tronbrowser.dev">GitHub</a> &middot; <a href="https://github.com/profullstack/tronbrowser.dev/releases">Releases</a></footer>
<div class="onion"><span class="dot" aria-hidden="true"></span><a href="http://5nlx2hye6lddy4zki4xv3oteoxgerfd3343haizoicmonfb2ediy5gyd.onion" rel="noopener noreferrer">http://5nlx2hye6lddy4zki4xv3oteoxgerfd3343haizoicmonfb2ediy5gyd.onion</a></div>
</div>
<script type="module" src="https://cdn.jsdelivr.net/npm/@profullstack/footer@latest/src/embed.js"></script>
</div>
<script src="/app.js?v=0.1.8"></script>
<!-- CrawlProof: privacy-friendly, cookieless analytics. -->
<script data-site="75ea2754-bc0e-435e-ad49-afd9beb64b72" src="https://crawlproof.com/stats.js" async></script>
<!--pit-footer--><!--/pit-footer-->
</body>
</html>
1 change: 1 addition & 0 deletions apps/web/public/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,5 @@
<a class="home" href="/">← back to tronbrowser.dev</a>
</div>
<script src="/login.js"></script>
<!--pit-footer--><!--/pit-footer-->
</body></html>
1 change: 1 addition & 0 deletions apps/web/public/privacy.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,6 @@ <h2>Contact</h2>
TronBrowser is published by <a href="https://profullstack.com">Profullstack, Inc.</a></p>

<p class="muted">TronBrowser is free and open source under the MIT License.</p>
<!--pit-footer--><!--/pit-footer-->
</body>
</html>
1 change: 1 addition & 0 deletions apps/web/public/push.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,6 @@ <h2>The protocol, for a self-hosted service</h2>
</table>
<p class="muted">Browser calls carry <code>Authorization: Bearer &lt;device secret&gt;</code>.
The source is in <a href="https://github.com/profullstack/tronbrowser.dev/tree/main/services/api/src/push">services/api/src/push</a>.</p>
<!--pit-footer--><!--/pit-footer-->
</body>
</html>
1 change: 1 addition & 0 deletions apps/web/public/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,5 @@ <h3 style="color:var(--muted);font-size:13px;margin-top:24px">Import / export OP
</div>

<script type="module" src="/settings.js"></script>
<!--pit-footer--><!--/pit-footer-->
</body></html>
51 changes: 51 additions & 0 deletions scripts/pit-footer.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// The pit.world global footer for the static site Caddy serves from /srv.
// Runs beside Caddy (start.sh): fetches the snippet server-side at boot and then
// hourly, and writes it between the <!--pit-footer--> markers each page carries
// just before </body>. Any failure keeps the last good copy (or nothing), so a
// pit.world outage never breaks a page. Writes are temp-file + rename (atomic).
import { readdir, readFile, rename, writeFile } from "node:fs/promises";
import { join } from "node:path";

const URL = "https://pit.world/snippets/footer.html?site=tronbrowser.dev";
const ROOT = process.env.PIT_FOOTER_ROOT || "/srv";
const EVERY_MS = 60 * 60 * 1000;
const MARKERS = /<!--pit-footer-->[\s\S]*?<!--\/pit-footer-->/;

async function htmlFiles(dir) {
const out = [];
for (const entry of await readdir(dir, { withFileTypes: true })) {
const path = join(dir, entry.name);
if (entry.isDirectory()) out.push(...(await htmlFiles(path)));
else if (entry.name.endsWith(".html")) out.push(path);
}
return out;
}

async function refresh() {
let snippet;
try {
const res = await fetch(URL, { signal: AbortSignal.timeout(3000) });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
snippet = (await res.text()).trim();
if (!snippet.includes("data-pit-footer")) throw new Error("no data-pit-footer");
} catch (err) {
console.error(`[pit-footer] fetch failed, keeping last copy: ${err.message}`);
return;
}
const block = `<!--pit-footer-->${snippet}<!--/pit-footer-->`;
for (const file of await htmlFiles(ROOT)) {
try {
const html = await readFile(file, "utf8");
if (!MARKERS.test(html)) continue;
const next = html.replace(MARKERS, () => block);
if (next === html) continue;
await writeFile(`${file}.pit-tmp`, next);
await rename(`${file}.pit-tmp`, file);
} catch (err) {
console.error(`[pit-footer] ${file}: ${err.message}`);
}
}
}

await refresh();
if (!process.argv.includes("--once")) setInterval(refresh, EVERY_MS);
3 changes: 3 additions & 0 deletions start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ set -e
# a transient DB hiccup shouldn't block the whole service from starting.
MIGRATIONS_DIR=/api/migrations-pg bun /api/db-migrate.mjs || echo "[migrate] FAILED — continuing"
PORT=8090 bun /api/dist/index.js &
# pit.world global footer: fetched server-side at boot and hourly, written into
# each page's <!--pit-footer--> slot. Never blocks or breaks serving.
bun /pit-footer.mjs &

# --- Tor v3 hidden service ---------------------------------------------------
# Expose the site over a stable .onion. Tor forwards onion:80 -> Caddy on $PORT
Expand Down
Loading