···11+Creative Commons Attribution 4.0 International (CC BY 4.0) License
22+33+Copyright (c) 2025 boop.cat
44+55+You are free to:
66+77+- Share — copy and redistribute the material in any medium or format
88+- Adapt — remix, transform, and build upon the material for any purpose, even commercially
99+1010+Under the following terms:
1111+1212+- **Attribution** — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
1313+1414+No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
1515+1616+**Full license text:** https://creativecommons.org/licenses/by/4.0/
+90
README.md
···11+# boop.cat codebase
22+33+This is boop.cat's codebase, and it was released under the [Creative Commons Attribution 4.0 International (CC BY 4.0) License](https://creativecommons.org/licenses/by/4.0/).
44+55+## Features
66+77+- **Instant Deployment**: Connect any public or private Git repository.
88+- **Auto-Deploy**: Automatically triggers a new build on every `push` to your main branch. (for GitHub only, use API for other platforms)
99+- **Edge Delivery**: Powered by Cloudflare Workers for global caching and low latency.
1010+- **Managed SSL**: Automatic HTTPS for every site and custom domain.
1111+- **Environment Variables**: Full support for build-time environment variables.
1212+- **Clean API**: Manage your sites and deployments programmatically.
1313+1414+## Tech Stack
1515+1616+- **Backend**: Go (chi router, SQLite)
1717+- **Frontend**: React (Vite, Lucide Icons)
1818+- **Storage**: Backblaze B2 (Object storage)
1919+- **Delivery**: Cloudflare Workers (Edge Computing) & KV (Metadata)
2020+- **Database**: SQLite (Local file-based database)
2121+2222+## Getting Started
2323+2424+### 1. Prerequisites
2525+2626+- [Go](https://go.dev/) 1.23+
2727+- [Node.js](https://nodejs.org/) 22+ (with `pnpm` or `bun`)
2828+- [Cloudflare Account](https://dash.cloudflare.com/) (Token, Account ID, Zone ID)
2929+- [Backblaze B2 Account](https://www.backblaze.com/b2/cloud-storage.html) (Key ID, App Key, Bucket)
3030+3131+### 2. Installation
3232+3333+```bash
3434+# Clone the repository
3535+git clone https://tangled.org/scanash.com/boombox
3636+cd boombox
3737+3838+# Install frontend dependencies
3939+npm install
4040+```
4141+4242+### 3. Configuration
4343+4444+Copy the example environment file and fill in your credentials:
4545+4646+```bash
4747+cp .env.example .env
4848+```
4949+5050+Key variables to configure:
5151+5252+- `SESSION_SECRET`: Random string for sessions.
5353+- `FSD_DATA_DIR`: Path where the SQLite database will be stored.
5454+- `CF_*`: Your Cloudflare API credentials.
5555+- `B2_*`: Your Backblaze B2 storage credentials.
5656+5757+### 4. Running Locally
5858+5959+**Start the Go Backend:**
6060+6161+```bash
6262+cd backend-go
6363+go run main.go
6464+```
6565+6666+The backend serves the frontend from `client/dist`. For development, you can run the Vite dev server separately:
6767+6868+**Start Vite Dev Server:**
6969+7070+```bash
7171+cd client
7272+npm run dev
7373+```
7474+7575+## Docker Deployment
7676+7777+The project includes a multi-stage `Dockerfile` that builds both the React frontend and Go backend into a single production-ready image.
7878+7979+```bash
8080+docker build -t boop-cat .
8181+docker run -p 8788:8788 --env-file .env boop-cat
8282+```
8383+8484+## API Documentation
8585+8686+The platform provides a REST API for managing sites. See the **API Documentation** page within the dashboard for details and examples.
8787+8888+## License
8989+9090+This project is licensed under the Creative Commons Attribution 4.0 International (CC BY 4.0) License. See [LICENSE](LICENSE) for details.
···11+import React from 'react';
22+import { Link } from 'react-router-dom';
33+import ThemeToggle from '../components/ThemeToggle.jsx';
44+55+export default function Dmca() {
66+ return (
77+ <div className="legal-page">
88+ <div className="legal-theme-toggle">
99+ <ThemeToggle />
1010+ </div>
1111+ <div className="legal-card">
1212+ <div className="legal-header">
1313+ <h1>DMCA Policy</h1>
1414+ <span className="legal-date">Last updated: December 19, 2025</span>
1515+ </div>
1616+1717+ <div className="legal-content">
1818+ <p>
1919+ boop.cat respects the intellectual property rights of others and expects its users to do the same. In
2020+ accordance with the Digital Millennium Copyright Act of 1998, the text of which may be found on the U.S.
2121+ Copyright Office website at{' '}
2222+ <a href="http://www.copyright.gov/legislation/dmca.pdf" target="_blank" rel="noopener noreferrer">
2323+ http:
2424+ </a>
2525+ , we will respond expeditiously to claims of copyright infringement committed using the Service.
2626+ </p>
2727+2828+ <h2>Reporting Copyright Infringement</h2>
2929+ <p>
3030+ If you are a copyright owner, or are authorized to act on behalf of one, or authorized to act under any
3131+ exclusive right under copyright, please report alleged copyright infringements taking place on or through
3232+ the Service by sending a notice to our designated agent at:
3333+ </p>
3434+3535+ <div className="legal-callout">
3636+ <strong>Email:</strong> <a href="mailto:dmca@boop.cat">dmca@boop.cat</a>
3737+ </div>
3838+3939+ <p>
4040+ Upon receipt of the Notice as described below, we will take whatever action, in our sole discretion, we deem
4141+ appropriate, including removal of the challenged material from the Site.
4242+ </p>
4343+4444+ <h2>What to include</h2>
4545+ <p>Please include the following in your notice:</p>
4646+ <ul>
4747+ <li>Identify the copyrighted work that you claim has been infringed.</li>
4848+ <li>
4949+ Identify the material that you claim is infringing (or to be the subject of infringing activity) and that
5050+ is to be removed or access to which is to be disabled, and information reasonably sufficient to permit us
5151+ to locate the material (e.g., the URL).
5252+ </li>
5353+ <li>Your contact information (email, address, phone number).</li>
5454+ <li>
5555+ A statement that you have a good faith belief that use of the material in the manner complained of is not
5656+ authorized by the copyright owner, its agent, or the law.
5757+ </li>
5858+ </ul>
5959+ </div>
6060+6161+ <div className="legal-footer">
6262+ <Link to="/">← Back to home</Link>
6363+ </div>
6464+ </div>
6565+ </div>
6666+ );
6767+}
···11+import React from 'react';
22+import { Link } from 'react-router-dom';
33+import ThemeToggle from '../components/ThemeToggle.jsx';
44+55+export default function Privacy() {
66+ return (
77+ <div className="legal-page">
88+ <div className="legal-theme-toggle">
99+ <ThemeToggle />
1010+ </div>
1111+ <div className="legal-card">
1212+ <div className="legal-header">
1313+ <h1>Privacy Policy</h1>
1414+ <span className="legal-date">Last updated: December 19, 2025</span>
1515+ </div>
1616+1717+ <div className="legal-content">
1818+ <p>
1919+ This Privacy Policy explains what information boop.cat (the "Service") collects, how we use it, and how it
2020+ may be processed by third-party infrastructure providers.
2121+ </p>
2222+2323+ <h2>1. Information we collect</h2>
2424+ <p>The Service may collect account and usage information such as:</p>
2525+ <ul>
2626+ <li>Email address and username (for authentication and account management).</li>
2727+ <li>OAuth identifiers (when you sign in with a third‑party provider).</li>
2828+ <li>IP addresses are hashed and stored for security and abuse prevention.</li>
2929+ <li>Basic request metadata (e.g., User-Agent) for rate limiting.</li>
3030+ <li>Repository URLs and deployment configuration you provide to create deployments.</li>
3131+ <li>Deployment logs generated during build and upload (to help you debug deployments).</li>
3232+ <li>Files you deploy (static site assets) and related identifiers needed to serve your site.</li>
3333+ </ul>
3434+3535+ <h2>2. How we use information</h2>
3636+ <p>
3737+ We use information to operate the Service, authenticate users, secure the platform, prevent abuse, and
3838+ support deployments.
3939+ </p>
4040+4141+ <h2>3. Where your deployed sites are stored and served</h2>
4242+ <p>
4343+ When you deploy a site, your static build output is uploaded to Backblaze B2 object storage and served
4444+ globally via Cloudflare. Cloudflare also stores limited routing metadata in Cloudflare Workers KV to map
4545+ your subdomain to your current deployment.
4646+ </p>
4747+4848+ <h2>4. Cookies and authentication</h2>
4949+ <p>
5050+ The Service uses an HTTP-only session cookie to keep you signed in. Session data may be stored on our
5151+ servers for account authentication and security.
5252+ </p>
5353+5454+ <h2>5. Sharing</h2>
5555+ <p>
5656+ We do not sell your personal information. We may share information with service providers as needed to
5757+ operate the Service (for example, email delivery), or when required by law.
5858+ </p>
5959+6060+ <p>
6161+ Our infrastructure providers (acting as service providers/processors) may process data necessary to deliver
6262+ the Service, including:
6363+ </p>
6464+ <ul>
6565+ <li>Cloudflare (CDN/Workers/KV) for request handling, caching, routing, and delivery of deployed sites.</li>
6666+ <li>Backblaze B2 for storage of deployed static site files.</li>
6767+ <li>
6868+ Hetzner for hosting the main Service application servers (dashboard/API), including build and deployment
6969+ orchestration.
7070+ </li>
7171+ </ul>
7272+7373+ <h2>6. Data retention</h2>
7474+ <p>
7575+ We retain account and deployment data for as long as necessary to provide the Service and for security and
7676+ operational purposes. When you delete a project, we attempt to remove its associated deployments, deployed
7777+ files, and routing metadata.
7878+ </p>
7979+8080+ <h2>7. Security</h2>
8181+ <p>We take reasonable measures to protect data, but no method of transmission or storage is 100% secure.</p>
8282+8383+ <h2>8. Contact</h2>
8484+ <p>
8585+ If you have questions about this policy, contact <a href="mailto:hello@boop.cat">hello@boop.cat</a>.
8686+ </p>
8787+ </div>
8888+8989+ <div className="legal-footer">
9090+ <Link to="/">← Back to home</Link>
9191+ </div>
9292+ </div>
9393+ </div>
9494+ );
9595+}
+143
client/src/pages/ResetPassword.jsx
···11+import React, { useState, useEffect } from 'react';
22+import { useSearchParams, useNavigate } from 'react-router-dom';
33+import ThemeToggle from '../components/ThemeToggle.jsx';
44+55+const ERROR_MESSAGES = {
66+ 'missing-token': 'Reset link is invalid.',
77+ 'invalid-token': 'Reset link is invalid or has already been used.',
88+ 'already-used': 'This reset link has already been used.',
99+ expired: 'This reset link has expired. Please request a new one.',
1010+ 'password-required': 'Please enter a new password.',
1111+ 'password-too-short': 'Password must be at least 8 characters.',
1212+ 'password-too-long': 'Password is too long.',
1313+ 'user-not-found': 'User not found.',
1414+ 'reset-failed': 'Failed to reset password. Please try again.'
1515+};
1616+1717+export default function ResetPassword() {
1818+ const [searchParams] = useSearchParams();
1919+ const navigate = useNavigate();
2020+ const token = searchParams.get('token');
2121+2222+ const [password, setPassword] = useState('');
2323+ const [confirmPassword, setConfirmPassword] = useState('');
2424+ const [loading, setLoading] = useState(false);
2525+ const [error, setError] = useState('');
2626+ const [success, setSuccess] = useState(false);
2727+2828+ useEffect(() => {
2929+ if (!token) {
3030+ setError('missing-token');
3131+ }
3232+ }, [token]);
3333+3434+ async function handleSubmit(e) {
3535+ e.preventDefault();
3636+ setError('');
3737+3838+ if (password !== confirmPassword) {
3939+ setError('Passwords do not match.');
4040+ return;
4141+ }
4242+4343+ if (password.length < 8) {
4444+ setError('password-too-short');
4545+ return;
4646+ }
4747+4848+ setLoading(true);
4949+ try {
5050+ const res = await fetch('/api/auth/reset-password', {
5151+ method: 'POST',
5252+ headers: { 'content-type': 'application/json' },
5353+ body: JSON.stringify({ token, newPassword: password })
5454+ });
5555+5656+ const data = await res.json().catch(() => null);
5757+5858+ if (!res.ok) {
5959+ const errCode = data?.error || 'reset-failed';
6060+ setError(ERROR_MESSAGES[errCode] || errCode);
6161+ return;
6262+ }
6363+6464+ setSuccess(true);
6565+ } finally {
6666+ setLoading(false);
6767+ }
6868+ }
6969+7070+ if (success) {
7171+ return (
7272+ <div className="auth-page">
7373+ <div className="auth-theme-toggle">
7474+ <ThemeToggle />
7575+ </div>
7676+ <div className="auth-card">
7777+ <div style={{ textAlign: 'center' }}>
7878+ <div style={{ fontSize: 64, marginBottom: 16 }}>✅</div>
7979+ <h1>Password reset!</h1>
8080+ <p className="subtitle" style={{ marginBottom: 24 }}>
8181+ Your password has been successfully changed.
8282+ </p>
8383+ <button className="btn primary" onClick={() => navigate('/login')} style={{ width: '100%' }}>
8484+ Log in with new password
8585+ </button>
8686+ </div>
8787+ </div>
8888+ </div>
8989+ );
9090+ }
9191+9292+ const displayError = ERROR_MESSAGES[error] || error;
9393+9494+ return (
9595+ <div className="auth-page">
9696+ <div className="auth-theme-toggle">
9797+ <ThemeToggle />
9898+ </div>
9999+ <div className="auth-card">
100100+ <h1>Set new password</h1>
101101+ <p className="subtitle">Enter your new password below.</p>
102102+103103+ <form className="form" onSubmit={handleSubmit}>
104104+ <input
105105+ className="input"
106106+ type="password"
107107+ placeholder="New password"
108108+ value={password}
109109+ onChange={(e) => setPassword(e.target.value)}
110110+ required
111111+ minLength={8}
112112+ disabled={loading || !token}
113113+ />
114114+ <input
115115+ className="input"
116116+ type="password"
117117+ placeholder="Confirm new password"
118118+ value={confirmPassword}
119119+ onChange={(e) => setConfirmPassword(e.target.value)}
120120+ required
121121+ minLength={8}
122122+ disabled={loading || !token}
123123+ />
124124+125125+ {displayError && <div className="error">{displayError}</div>}
126126+127127+ <button
128128+ className="btn primary"
129129+ type="submit"
130130+ disabled={loading || !token || !password || !confirmPassword}
131131+ style={{ width: '100%' }}
132132+ >
133133+ {loading ? 'Resetting...' : 'Reset password'}
134134+ </button>
135135+ </form>
136136+137137+ <div className="auth-footer">
138138+ <a href="/login">Back to login</a>
139139+ </div>
140140+ </div>
141141+ </div>
142142+ );
143143+}
···11+import React from 'react';
22+import { Link } from 'react-router-dom';
33+import ThemeToggle from '../components/ThemeToggle.jsx';
44+55+export default function Tos() {
66+ return (
77+ <div className="legal-page">
88+ <div className="legal-theme-toggle">
99+ <ThemeToggle />
1010+ </div>
1111+ <div className="legal-card">
1212+ <div className="legal-header">
1313+ <h1>Terms of Service</h1>
1414+ <span className="legal-date">Last updated: December 18, 2025</span>
1515+ </div>
1616+1717+ <div className="legal-content">
1818+ <p>
1919+ These Terms of Service ("Terms") govern your use of boop.cat (the "Service"). By using the Service, you
2020+ agree to these Terms.
2121+ </p>
2222+2323+ <h2>1. Accounts</h2>
2424+ <p>
2525+ You are responsible for maintaining the security of your account and for all activity that occurs under your
2626+ account.
2727+ </p>
2828+2929+ <h2>2. Acceptable use</h2>
3030+ <p>
3131+ You agree not to use the Service to host, deploy, distribute, or link to content that is illegal, malicious,
3232+ infringes intellectual property, violates privacy, or is intended to harm or disrupt systems (including
3333+ malware, phishing, or abuse of third‑party services).
3434+ </p>
3535+3636+ <h2>3. Deployments and content</h2>
3737+ <p>
3838+ You retain responsibility for the repositories you connect and the content you deploy. We may suspend or
3939+ remove deployments that violate these Terms or applicable law.
4040+ </p>
4141+4242+ <h2>4. Rate limits and availability</h2>
4343+ <p>
4444+ The Service may enforce rate limits, quotas, and other restrictions. The Service is provided on an "as is"
4545+ and "as available" basis and may change or be discontinued.
4646+ </p>
4747+4848+ <h2>5. Termination</h2>
4949+ <p>
5050+ We may suspend or terminate access to the Service at any time for violations of these Terms, suspected
5151+ abuse, or security reasons.
5252+ </p>
5353+5454+ <h2>6. Disclaimer</h2>
5555+ <p>
5656+ To the maximum extent permitted by law, we disclaim all warranties and will not be liable for any indirect,
5757+ incidental, or consequential damages arising from your use of the Service.
5858+ </p>
5959+6060+ <h2>7. Contact</h2>
6161+ <p>
6262+ Questions about these Terms can be directed to <a href="mailto:hello@boop.cat">hello@boop.cat</a>.
6363+ </p>
6464+ </div>
6565+6666+ <div className="legal-footer">
6767+ <Link to="/">← Back to home</Link>
6868+ </div>
6969+ </div>
7070+ </div>
7171+ );
7272+}