diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000000..53f198b041 --- /dev/null +++ b/.github/workflows/pages.yml @@ -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 diff --git a/scripts/check-pages-site.py b/scripts/check-pages-site.py new file mode 100755 index 0000000000..7b395df3c8 --- /dev/null +++ b/scripts/check-pages-site.py @@ -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 '' not in text or ' {target.relative_to(SITE)}" + ) + + print(f"Validated {len(pages)} HTML pages and their local links.") + + +if __name__ == "__main__": + main() diff --git a/site/.nojekyll b/site/.nojekyll new file mode 100644 index 0000000000..e69de29bb2 diff --git a/site/assets/style.css b/site/assets/style.css new file mode 100644 index 0000000000..7d5cee1f65 --- /dev/null +++ b/site/assets/style.css @@ -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; } diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000000..be2ed26f1e --- /dev/null +++ b/site/index.html @@ -0,0 +1,25 @@ + + + + + + + Buzz — Privacy and Support + + + +
+
+
Buzz
+

Privacy and support

+

Information for people using the Buzz collaboration app.

+ +
+ +
+ + diff --git a/site/privacy/index.html b/site/privacy/index.html new file mode 100644 index 0000000000..de85dc5a53 --- /dev/null +++ b/site/privacy/index.html @@ -0,0 +1,82 @@ + + + + + + + Buzz Privacy Policy + + + +
+
+
Buzz
+

Privacy policy

+

Effective July 14, 2026

+

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.

+ +
+ +
+

How Buzz works

+

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.

+
+ +
+

Information handled by Buzz

+

Depending on how you use Buzz, the app and your selected relay may handle:

+ +

Buzz uses this information to connect to your workspace, deliver collaboration features, maintain app state, secure the service, and troubleshoot problems.

+
+ +
+

Information stored on your device

+

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.

+

Your Nostr private key controls your identity. Do not share it. Anyone who has it may be able to act as you.

+
+ +
+

Sharing and visibility

+

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.

+

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.

+
+ +
+

Retention and deletion

+

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.

+

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.

+
+ +
+

Security

+

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.

+
+ +
+

Children

+

Buzz is intended for workplace and developer collaboration. It is not directed to children.

+
+ +
+

Changes to this policy

+

We may update this policy as Buzz changes. We will post revisions on this page and update the effective date above.

+
+ +
+

Contact

+

For general, non-sensitive questions about this policy, use the public contact option on the Buzz support page. 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.

+
+ + +
+ + diff --git a/site/support/index.html b/site/support/index.html new file mode 100644 index 0000000000..2d9652df8a --- /dev/null +++ b/site/support/index.html @@ -0,0 +1,48 @@ + + + + + + + Buzz Support + + + +
+
+
Buzz
+

Support

+

Get help with the Buzz mobile and desktop applications.

+ +
+ +
+

Get help or report a bug

+

Use the public Buzz issue tracker to report a reproducible problem or request a feature. Before opening a new issue, search existing issues for a solution.

+

Include your Buzz version, device and operating-system version, what you expected, what happened, and steps that reproduce the problem.

+

Do not include private keys, access tokens, passwords, private messages, personal information, or other sensitive data in a public issue.

+
+ +
+

Workspace and account access

+

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.

+
+ +
+

Security vulnerabilities

+

Do not disclose a suspected security vulnerability in a public issue. Follow the private reporting instructions in the repository's security policy.

+
+ +
+

Privacy questions

+

For questions about the Buzz privacy policy, open a public support issue 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.

+
+ + +
+ +