Skip to content
Closed
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
55 changes: 55 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Deploy GitHub Pages

on:
pull_request:
paths:
- ".github/workflows/pages.yml"
- "scripts/check-pages-site.py"
- "site/**"
push:
branches: [main]
paths:
- ".github/workflows/pages.yml"
- "scripts/check-pages-site.py"
- "site/**"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: pages-${{ github.event_name == 'pull_request' && github.event.pull_request.number || 'deploy' }}
cancel-in-progress: false

jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
- name: Validate site
run: python3 scripts/check-pages-site.py

deploy:
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: validate
permissions:
contents: read
pages: write
id-token: write
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
- name: Configure Pages
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b
- name: Upload site artifact
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa
with:
path: site
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e
65 changes: 65 additions & 0 deletions scripts/check-pages-site.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""Validate the static Buzz support site without external dependencies."""

from html.parser import HTMLParser
from pathlib import Path
from urllib.parse import urlparse

SITE = Path(__file__).resolve().parents[1] / "site"


class LinkParser(HTMLParser):
def __init__(self) -> None:
super().__init__()
self.links: list[str] = []

def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
attributes = dict(attrs)
if tag in {"a", "link"}:
attribute = "href"
elif tag in {"img", "script", "source"}:
attribute = "src"
else:
attribute = None
value = attributes.get(attribute) if attribute else None
if value:
self.links.append(value)


def resolve_local_link(page: Path, link: str) -> Path | None:
parsed = urlparse(link)
if parsed.scheme or parsed.netloc or link.startswith("#"):
return None
target = (page.parent / parsed.path).resolve()
if not target.is_relative_to(SITE.resolve()):
raise AssertionError(f"{page.relative_to(SITE)}: link escapes site: {link}")
if parsed.path.endswith("/") or target.is_dir():
target /= "index.html"
return target


def main() -> None:
pages = sorted(SITE.rglob("*.html"))
required = {SITE / "index.html", SITE / "privacy/index.html", SITE / "support/index.html"}
missing = required.difference(pages)
if missing:
raise AssertionError(f"missing required pages: {sorted(str(path) for path in missing)}")

for page in pages:
text = page.read_text(encoding="utf-8")
parser = LinkParser()
parser.feed(text)
if '<html lang="en">' not in text or '<meta name="viewport"' not in text:
raise AssertionError(f"{page.relative_to(SITE)}: missing required document metadata")
for link in parser.links:
target = resolve_local_link(page, link)
if target is not None and not target.is_file():
raise AssertionError(
f"{page.relative_to(SITE)}: broken local link {link} -> {target.relative_to(SITE)}"
)

print(f"Validated {len(pages)} HTML pages and their local links.")


if __name__ == "__main__":
main()
Empty file added site/.nojekyll
Empty file.
72 changes: 72 additions & 0 deletions site/assets/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
:root {
color-scheme: light dark;
--background: #f7f7f5;
--surface: #ffffff;
--text: #20201f;
--muted: #66645f;
--border: #d9d7d0;
--accent: #6d4aff;
--accent-hover: #5836e6;
}

@media (prefers-color-scheme: dark) {
:root {
--background: #171716;
--surface: #222220;
--text: #f3f2ee;
--muted: #aaa79f;
--border: #3b3935;
--accent: #a58cff;
--accent-hover: #baa7ff;
}
}

* { box-sizing: border-box; }

body {
margin: 0;
background: var(--background);
color: var(--text);
font: 1rem/1.65 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

main {
width: min(46rem, calc(100% - 2rem));
margin: 0 auto;
padding: 3rem 0 5rem;
}

header, section, .card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 0.75rem;
padding: 1.5rem;
margin-bottom: 1rem;
}

header { padding: 2rem 1.5rem; }

h1, h2 { line-height: 1.25; }
h1 { margin: 0 0 0.5rem; font-size: clamp(2rem, 7vw, 3rem); }
h2 { margin-top: 0; font-size: 1.25rem; }
p:last-child, ul:last-child { margin-bottom: 0; }
ul { padding-left: 1.25rem; }
li + li { margin-top: 0.4rem; }

.eyebrow, .updated, footer {
color: var(--muted);
font-size: 0.9rem;
}

.eyebrow {
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}

a { color: var(--accent); text-underline-offset: 0.18em; }
a:hover { color: var(--accent-hover); }

nav { display: flex; flex-wrap: wrap; gap: 0.75rem 1rem; margin-top: 1.25rem; }
footer { padding: 1rem 0; }
code { overflow-wrap: anywhere; }
25 changes: 25 additions & 0 deletions site/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Privacy and support information for Buzz.">
<title>Buzz — Privacy and Support</title>
<link rel="stylesheet" href="assets/style.css">
</head>
<body>
<main>
<header>
<div class="eyebrow">Buzz</div>
<h1>Privacy and support</h1>
<p>Information for people using the Buzz collaboration app.</p>
<nav aria-label="Legal and support pages">
<a href="privacy/">Privacy policy</a>
<a href="support/">Support</a>
<a href="https://github.com/block/buzz">Source code</a>
</nav>
</header>
<footer>Buzz is an open-source project from Block, Inc.</footer>
</main>
</body>
</html>
82 changes: 82 additions & 0 deletions site/privacy/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Privacy policy for the Buzz collaboration app.">
<title>Buzz Privacy Policy</title>
<link rel="stylesheet" href="../assets/style.css">
</head>
<body>
<main>
<header>
<div class="eyebrow">Buzz</div>
<h1>Privacy policy</h1>
<p class="updated">Effective July 14, 2026</p>
<p>This policy describes how the Buzz mobile and desktop applications handle information. Buzz is an open-source collaboration app from Block, Inc. that connects people and software agents through a Nostr-compatible relay.</p>
<nav aria-label="Related pages">
<a href="../">Home</a>
<a href="../support/">Support</a>
<a href="https://github.com/block/buzz">Source code</a>
</nav>
</header>

<section>
<h2>How Buzz works</h2>
<p>Buzz connects to a relay associated with a workspace. A relay may be operated by Block, your organization, or another party. The operator of the relay you use controls that relay's storage, access, and retention practices. If another organization provides your workspace, contact that organization for details about its practices.</p>
</section>

<section>
<h2>Information handled by Buzz</h2>
<p>Depending on how you use Buzz, the app and your selected relay may handle:</p>
<ul>
<li><strong>Identity and workspace information</strong>, such as a Nostr public key, display name, avatar, workspace name, relay address, and membership or role information.</li>
<li><strong>Content you provide</strong>, including messages, reactions, status information, channel content, and files, photos, or videos you choose to upload.</li>
<li><strong>Collaboration activity</strong>, such as message timestamps, read state, channel membership, replies, mentions, and agent activity needed to provide app features.</li>
<li><strong>Connection and operational information</strong>, such as network requests, relay authentication events, and server logs that a relay operator may process to secure, operate, and troubleshoot its service.</li>
</ul>
<p>Buzz uses this information to connect to your workspace, deliver collaboration features, maintain app state, secure the service, and troubleshoot problems.</p>
</section>

<section>
<h2>Information stored on your device</h2>
<p>The app stores workspace connection details and your Nostr identity credential on your device. On mobile devices, workspace credentials are stored using the operating system's secure-storage facility. The app also stores local preferences, such as theme, channel organization, mute or star choices, and read state.</p>
<p>Your Nostr private key controls your identity. Do not share it. Anyone who has it may be able to act as you.</p>
</section>

<section>
<h2>Sharing and visibility</h2>
<p>Messages, profile information, uploaded media, and other collaboration activity are sent to your selected relay and may be visible to workspace members according to the workspace's permissions. Content may also be processed by software agents or service providers that you or your workspace operator choose to use.</p>
<p>Buzz does not include advertising SDKs or third-party analytics SDKs in its mobile app, and the app does not use this information for targeted advertising.</p>
</section>

<section>
<h2>Retention and deletion</h2>
<p>Retention depends on the relay and workspace you use. Buzz lets you request deletion of your own messages where the relay and your permissions support it. Removing a workspace from the mobile app deletes that workspace's locally stored credentials and connection information from the app, but does not by itself delete information already sent to a relay or copies visible to other participants.</p>
<p>Because Buzz uses a distributed protocol, a deletion request sent to one relay may not remove copies previously stored by other relays, participants, agents, backups, or external systems. For requests concerning relay-hosted data, contact the operator of your workspace or relay.</p>
</section>

<section>
<h2>Security</h2>
<p>Buzz uses cryptographic signing for Nostr events and supports encrypted network connections to relays. No system is completely secure. Protect your device and private key, use a relay operator you trust, and avoid sharing sensitive information in public issue reports.</p>
</section>

<section>
<h2>Children</h2>
<p>Buzz is intended for workplace and developer collaboration. It is not directed to children.</p>
</section>

<section>
<h2>Changes to this policy</h2>
<p>We may update this policy as Buzz changes. We will post revisions on this page and update the effective date above.</p>
</section>

<section>
<h2>Contact</h2>
<p>For general, non-sensitive questions about this policy, use the public contact option on the <a href="../support/">Buzz support page</a>. Do not post personal or sensitive information in a public issue. For access, deletion, or other requests concerning data controlled by your workspace or relay operator, contact that operator directly.</p>
</section>

<footer><a href="../">Buzz privacy and support</a></footer>
</main>
</body>
</html>
48 changes: 48 additions & 0 deletions site/support/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Support options for the Buzz collaboration app.">
<title>Buzz Support</title>
<link rel="stylesheet" href="../assets/style.css">
</head>
<body>
<main>
<header>
<div class="eyebrow">Buzz</div>
<h1>Support</h1>
<p>Get help with the Buzz mobile and desktop applications.</p>
<nav aria-label="Related pages">
<a href="../">Home</a>
<a href="../privacy/">Privacy policy</a>
<a href="https://github.com/block/buzz">Source code</a>
</nav>
</header>

<section>
<h2>Get help or report a bug</h2>
<p>Use the public <a href="https://github.com/block/buzz/issues/new/choose">Buzz issue tracker</a> to report a reproducible problem or request a feature. Before opening a new issue, <a href="https://github.com/block/buzz/issues">search existing issues</a> for a solution.</p>
<p>Include your Buzz version, device and operating-system version, what you expected, what happened, and steps that reproduce the problem.</p>
<p><strong>Do not include private keys, access tokens, passwords, private messages, personal information, or other sensitive data in a public issue.</strong></p>
</section>

<section>
<h2>Workspace and account access</h2>
<p>Buzz connects to a workspace through a relay. If you cannot pair, connect, access a channel, or manage workspace-hosted data, contact the administrator or relay operator that provided your workspace. The open-source maintainers cannot restore a lost Nostr private key or change permissions on a relay they do not operate.</p>
</section>

<section>
<h2>Security vulnerabilities</h2>
<p>Do not disclose a suspected security vulnerability in a public issue. Follow the private reporting instructions in the repository's <a href="https://github.com/block/buzz/security/policy">security policy</a>.</p>
</section>

<section>
<h2>Privacy questions</h2>
<p>For questions about the <a href="../privacy/">Buzz privacy policy</a>, open a <a href="https://github.com/block/buzz/issues/new/choose">public support issue</a> only if the question contains no sensitive information. For data stored by your workspace or relay, contact that workspace's administrator or relay operator directly.</p>
</section>

<footer><a href="../">Buzz privacy and support</a></footer>
</main>
</body>
</html>
Loading