{"id":31956339,"url":"https://github.com/profullstack/unyunddit","last_synced_at":"2025-10-14T14:50:00.057Z","repository":{"id":317175489,"uuid":"1066285488","full_name":"profullstack/unyunddit","owner":"profullstack","description":"Reddit clone for .onion","archived":false,"fork":false,"pushed_at":"2025-09-29T11:33:43.000Z","size":124,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-29T11:37:19.714Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"wtfpl","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/profullstack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-29T09:27:28.000Z","updated_at":"2025-09-29T11:33:46.000Z","dependencies_parsed_at":"2025-09-29T11:37:23.756Z","dependency_job_id":"899ad5aa-5ca6-4cdd-a468-77e8f9f82c6c","html_url":"https://github.com/profullstack/unyunddit","commit_stats":null,"previous_names":["profullstack/unyunddit"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/profullstack/unyunddit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/profullstack%2Funyunddit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/profullstack%2Funyunddit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/profullstack%2Funyunddit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/profullstack%2Funyunddit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/profullstack","download_url":"https://codeload.github.com/profullstack/unyunddit/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/profullstack%2Funyunddit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279019141,"owners_count":26086685,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-14T14:48:36.294Z","updated_at":"2025-10-14T14:50:00.048Z","avatar_url":"https://github.com/profullstack.png","language":"JavaScript","readme":"\u003cdiv align=\"center\"\u003e\n\n# Unyunddit - Anonymous Reddit Clone\n\n![SvelteKit](https://img.shields.io/badge/SvelteKit-FF3E00?style=for-the-badge\u0026logo=svelte\u0026logoColor=white)\n![Node.js](https://img.shields.io/badge/Node.js-339933?style=for-the-badge\u0026logo=node.js\u0026logoColor=white)\n![Supabase](https://img.shields.io/badge/Supabase-3ECF8E?style=for-the-badge\u0026logo=supabase\u0026logoColor=white)\n![PostgreSQL](https://img.shields.io/badge/PostgreSQL-316192?style=for-the-badge\u0026logo=postgresql\u0026logoColor=white)\n![Docker](https://img.shields.io/badge/Docker-2496ED?style=for-the-badge\u0026logo=docker\u0026logoColor=white)\n![Tor](https://img.shields.io/badge/Tor_Network-7D4698?style=for-the-badge\u0026logo=tor-browser\u0026logoColor=white)\n\n[![License: WTFPL](https://img.shields.io/badge/License-WTFPL-brightgreen.svg)](http://www.wtfpl.net/about/)\n[![Node.js Version](https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen)](https://nodejs.org/)\n[![pnpm](https://img.shields.io/badge/pnpm-%3E%3D8.0.0-orange)](https://pnpm.io/)\n\n\u003c/div\u003e\n\nA completely anonymous Reddit clone designed specifically for the Tor network, featuring automatic post deletion after 72 hours. Built with SvelteKit and Supabase, it uses server-side rendering only (no client-side JavaScript) to maximize privacy and security.\n\n## 🌟 Features\n\n- **Complete Anonymity**: No user accounts, registration, or tracking required\n- **Auto-Deletion**: All posts and comments automatically delete after 72 hours\n- **Tor-Optimized**: Designed specifically for .onion websites and Tor browsers\n- **Server-Side Only**: No client-side JavaScript for enhanced security\n- **Anonymous Voting**: IP-based voting system with SHA256 hashed IPs for privacy\n- **Nested Comments**: Support for threaded discussions up to 10 levels deep\n- **Security Headers**: Strict CSP and privacy-focused HTTP headers\n\n## 🛠️ Tech Stack\n\n| Component | Technology |\n|-----------|-----------|\n| **Frontend** | SvelteKit (SSR-only mode) |\n| **Backend** | Node.js 20+ with ESM modules |\n| **Database** | Supabase (PostgreSQL) |\n| **Package Manager** | pnpm |\n| **Security** | Strict Content Security Policy, no client-side JS |\n\n## 🚀 Quick Start\n\n### Prerequisites\n\nBefore you begin, ensure you have the following installed:\n\n- **Node.js**: Version 20 or newer ([Download here](https://nodejs.org/))\n- **pnpm**: Version 8 or newer ([Installation guide](https://pnpm.io/installation))\n- **Supabase Account**: Create a free account at [supabase.com](https://supabase.com)\n\n### Installation\n\nFollow these steps to set up the project locally:\n\n#### 1. Clone the Repository\n\n```bash\ngit clone https://github.com/profullstack/unyunddit.git\ncd unyunddit\n```\n\n#### 2. Install Dependencies\n\n```bash\npnpm install\n```\n\n#### 3. Set Up Environment Variables\n\nCopy the example environment file and configure it:\n\n```bash\ncp .env.example .env\n```\n\nEdit the `.env` file with your Supabase credentials:\n\n```env\n# Supabase Configuration\nSUPABASE_URL=https://your-project-ref.supabase.co\nSUPABASE_ANON_KEY=your-anon-key-here\nSUPABASE_SERVICE_ROLE_KEY=your-service-role-key-here\nSUPABASE_DB_PASSWORD=your-database-password\n\n# JWT Configuration\nJWT_SECRET=your-secret-key-here\n\n# Port Configuration (optional)\nPORT=8000\nNGINX_PORT=8080\n\n# Post Deletion Configuration (optional)\nDELETE_FREQUENCY=72\n```\n\n\u003e **💡 Tip**: You can find your Supabase credentials in your project's API settings at [app.supabase.com](https://app.supabase.com).\n\n#### 4. Initialize the Database\n\nRun the database migrations to set up the required tables:\n\n```bash\npnpm run db:reset\n```\n\n#### 5. Start the Development Server\n\n```bash\npnpm run dev\n```\n\nYour application will be available at `http://localhost:5173`\n\n## 📊 Database Schema\n\nThe application uses three main tables for data management:\n\n### Posts Table\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `id` | UUID | Unique identifier |\n| `title` | VARCHAR(300) | Post title |\n| `content` | TEXT(10,000) | Post text content (optional) |\n| `url` | VARCHAR(2,000) | External link (optional) |\n| `upvotes` | INTEGER | Number of upvotes |\n| `downvotes` | INTEGER | Number of downvotes |\n| `comment_count` | INTEGER | Total number of comments |\n| `created_at` | TIMESTAMP | Creation timestamp |\n| `expires_at` | TIMESTAMP | Expiration timestamp (72 hours) |\n\n### Comments Table\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `id` | UUID | Unique identifier |\n| `post_id` | UUID | Reference to parent post |\n| `parent_id` | UUID | Reference to parent comment (for nesting) |\n| `content` | TEXT(5,000) | Comment text content |\n| `upvotes` | INTEGER | Number of upvotes |\n| `downvotes` | INTEGER | Number of downvotes |\n| `depth` | INTEGER | Nesting level (maximum 10) |\n| `created_at` | TIMESTAMP | Creation timestamp |\n| `expires_at` | TIMESTAMP | Expiration timestamp (72 hours) |\n\n### Votes Table\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `id` | UUID | Unique identifier |\n| `ip_hash` | VARCHAR(64) | SHA256 hash of voter's IP address |\n| `post_id` | UUID | Reference to voted post (nullable) |\n| `comment_id` | UUID | Reference to voted comment (nullable) |\n| `vote_type` | ENUM | Vote type ('up' or 'down') |\n| `created_at` | TIMESTAMP | Creation timestamp |\n| `expires_at` | TIMESTAMP | Expiration timestamp (72 hours) |\n\n## 🔐 Security Features\n\n### Privacy Protection\n\n- **No Personal Data**: Zero user accounts or personal data collection\n- **IP Hashing**: IP addresses are hashed using SHA256 for anonymous voting\n- **Strict CSP**: Content Security Policy blocks all client-side JavaScript\n- **No Referrer Leaks**: Referrer headers are not sent to external sites\n- **Anonymous Server**: Server identification headers are removed\n\n### Tor Network Optimization\n\n- **Pure SSR**: Server-side rendering only, no client-side JavaScript\n- **Minimal Dependencies**: Reduced external dependencies for faster loading\n- **Privacy Headers**: HTTP headers configured for maximum privacy\n- **No Tracking**: Absolutely no analytics, cookies, or tracking mechanisms\n\n### Automatic Cleanup\n\n- **Time-Based Deletion**: All content automatically expires after 72 hours\n- **Automated Process**: PostgreSQL cron jobs handle cleanup automatically\n- **Cascading Deletes**: Related data (votes, comments) are properly cleaned up\n- **Configurable**: Deletion frequency can be adjusted via environment variables\n\n## 🔗 API Endpoints\n\n### Pages\n\n| Route | Method | Description |\n|-------|--------|-------------|\n| `/` | GET | Home page with posts sorted by score |\n| `/new` | GET | New posts sorted by creation time |\n| `/popular` | GET | Popular posts with high activity |\n| `/submit` | GET | Post submission form |\n| `/post/[id]` | GET | Individual post with comments thread |\n\n### Actions\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/?/upvote` | POST | Upvote a post on home page |\n| `/?/downvote` | POST | Downvote a post on home page |\n| `/submit?/submit` | POST | Create a new post |\n| `/post/[id]?/comment` | POST | Add comment to a post |\n| `/post/[id]?/upvoteComment` | POST | Upvote a specific comment |\n| `/post/[id]?/downvoteComment` | POST | Downvote a specific comment |\n\n\u003e **📝 Note**: All actions use SvelteKit's form actions for server-side processing without JavaScript.\n\n## 💻 Development\n\n### Project Structure\n\n```\nsrc/\n├── lib/                     # Shared utilities and components\n│   ├── supabase.js         # Database client configuration\n│   ├── auth.js             # Authentication utilities\n│   ├── categories.js       # Category management\n│   ├── posts.js           # Post-related functions\n│   ├── voting.js          # Voting system logic\n│   ├── sanitize.js        # Content sanitization\n│   └── components/        # Reusable Svelte components\n├── routes/                 # SvelteKit pages and API routes\n│   ├── +layout.svelte     # Base layout template\n│   ├── +page.svelte       # Home page component\n│   ├── +page.server.js    # Home page server logic\n│   ├── new/               # New posts page\n│   ├── popular/           # Popular posts page\n│   ├── submit/            # Post submission page\n│   ├── post/[id]/         # Individual post pages\n│   └── api/               # API endpoints\n├── hooks.server.js         # Global server hooks (security headers)\n├── app.html               # HTML document template\n└── app.d.ts               # TypeScript definitions\n```\n\n### Available Commands\n\n#### Development\n```bash\npnpm run dev              # Start development server with hot reload\npnpm run build            # Build application for production\npnpm run preview          # Preview production build locally\npnpm run start            # Start production server\n```\n\n#### Code Quality\n```bash\npnpm run test             # Run test suite\npnpm run test:run         # Run tests once (no watch mode)\npnpm run test:ui          # Run tests with UI interface\npnpm run test:coverage    # Generate test coverage report\npnpm run lint             # Check code for linting errors\npnpm run lint:fix         # Fix automatically fixable linting errors\npnpm run format           # Format code using Prettier\npnpm run format:check     # Check code formatting\n```\n\n#### Database Management\n```bash\npnpm run db:reset         # Reset database and run all migrations\npnpm run db:migrate       # Create a new database migration\n```\n\n#### Docker Operations\n```bash\npnpm run docker:build     # Build Docker image\npnpm run docker:up        # Start services with Docker Compose\npnpm run docker:down      # Stop Docker Compose services\n```\n\n#### Deployment\n```bash\npnpm run deploy:railway        # Deploy to Railway platform\npnpm run deploy:digitalocean   # Deploy to DigitalOcean\n```\n\n## 🚢 Deployment\n\nChoose your preferred deployment method below:\n\n### Docker Deployment\n\nThe easiest way to deploy is using Docker:\n\n#### Build and Run Locally\n```bash\n# Build the Docker image\ndocker build -t unyunddit .\n\n# Run the container\ndocker run -p 8000:8000 --env-file .env unyunddit\n```\n\n#### Using Docker Compose\n```bash\n# Start all services\npnpm run docker:up\n\n# Stop all services\npnpm run docker:down\n```\n\n### Railway Platform\n\nRailway offers simple deployment with automatic builds:\n\n#### Prerequisites\n- Railway account ([railway.app](https://railway.app))\n- Railway CLI installed\n\n#### Deploy\n```bash\n# Automated deployment script\npnpm run deploy:railway\n\n# Or manually\nrailway login\nrailway link\nrailway up\n```\n\n### DigitalOcean\n\nDeploy to a DigitalOcean droplet for full control:\n\n#### Prerequisites\n- DigitalOcean account\n- DigitalOcean CLI (`doctl`) installed and configured\n\n#### Deploy\n```bash\n# Automated deployment script\npnpm run deploy:digitalocean\n\n# Or create droplet manually\npnpm run droplet:create\n```\n\n### Environment Variables for Production\n\nEnsure these environment variables are set in your production environment:\n\n```env\n# Required\nSUPABASE_URL=your_production_supabase_url\nSUPABASE_ANON_KEY=your_production_anon_key\nSUPABASE_SERVICE_ROLE_KEY=your_production_service_key\n\n# Optional\nPORT=8000\nDELETE_FREQUENCY=72\nNODE_ENV=production\n```\n\n\u003e **⚠️ Security Note**: Never commit your `.env` file to version control. Use your platform's environment variable settings for production deployments.\n\n## ⚙️ Configuration\n\n### Environment Variables\n\n| Variable | Required | Description | Example |\n|----------|----------|-------------|---------|\n| `SUPABASE_URL` | Yes | Your Supabase project URL | `https://abc123.supabase.co` |\n| `SUPABASE_ANON_KEY` | Yes | Supabase anonymous key (public) | `eyJ0eXAiOiJKV1QiLCJhbGciOiJI...` |\n| `SUPABASE_SERVICE_ROLE_KEY` | Yes | Supabase service role key (private) | `eyJ0eXAiOiJKV1QiLCJhbGciOiJI...` |\n| `SUPABASE_DB_PASSWORD` | Yes | Database password | `your-secure-password` |\n| `JWT_SECRET` | Yes | JWT signing secret | `your-jwt-secret-key` |\n| `PORT` | No | Server port (default: 8000) | `8000` |\n| `NGINX_PORT` | No | Nginx port (default: 8080) | `8080` |\n| `DELETE_FREQUENCY` | No | Hours before content deletion (default: 72) | `72` |\n\n### Security Headers\n\nThe application automatically sets strict security headers via `hooks.server.js`:\n\n| Header | Value | Purpose |\n|--------|-------|---------|\n| **Content-Security-Policy** | `default-src 'self'; script-src 'none'` | Blocks all JavaScript execution |\n| **Referrer-Policy** | `no-referrer` | Prevents referrer information leakage |\n| **X-Frame-Options** | `DENY` | Prevents clickjacking attacks |\n| **X-Content-Type-Options** | `nosniff` | Prevents MIME type sniffing |\n| **Permissions-Policy** | `geolocation=(), camera=(), microphone=()` | Blocks hardware access |\n\n\u003e **🔒 Privacy Focus**: These headers ensure maximum privacy and security for Tor users.\n\n## 🤝 Contributing\n\nWe welcome contributions to improve Unyunddit! Here's how you can help:\n\n### Contribution Guidelines\n\n1. **Fork the Repository**\n   ```bash\n   # Fork on GitHub, then clone your fork\n   git clone https://github.com/profullstack/unyunddit.git\n   cd unyunddit\n   ```\n\n2. **Create a Feature Branch**\n   ```bash\n   git checkout -b feature/your-feature-name\n   # or for bug fixes\n   git checkout -b fix/bug-description\n   ```\n\n3. **Make Your Changes**\n   - Follow the existing code style\n   - Add tests for new functionality\n   - Update documentation as needed\n\n4. **Run Quality Checks**\n   ```bash\n   # Run tests\n   pnpm run test\n\n   # Check linting\n   pnpm run lint\n\n   # Check formatting\n   pnpm run format:check\n\n   # Verify build works\n   pnpm run build\n   ```\n\n5. **Commit Your Changes**\n   ```bash\n   git add .\n   git commit -m \"feat: add your feature description\"\n   # Follow conventional commits: feat:, fix:, docs:, etc.\n   ```\n\n6. **Submit a Pull Request**\n   - Push to your fork\n   - Create a pull request with a clear description\n   - Link any related issues\n\n### Areas for Contribution\n\n- **Bug Fixes**: Help identify and fix issues\n- **Documentation**: Improve guides and API documentation\n- **UI/UX**: Enhance the user interface and experience\n- **Security**: Strengthen privacy and security features\n- **Performance**: Optimize loading times and efficiency\n- **Accessibility**: Improve accessibility for all users\n\n### Code Style\n\n- Use **Prettier** for formatting\n- Follow **ESLint** rules\n- Write meaningful commit messages\n- Add comments for complex logic\n- Include tests for new features\n\n### Need Help?\n\n- Check existing [issues](https://github.com/profullstack/unyunddit/issues)\n- Join discussions in pull requests\n- Create an issue if you're unsure about something\n\n## 📄 License\n\nThis project is licensed under the **WTFPL License** - see the [LICENSE](LICENSE) file for details.\n\n\u003e **What is WTFPL?** The \"Do What The F*ck You Want To Public License\" is a very permissive license that allows you to do anything with this code.\n\n## 🔒 Privacy Notice\n\nUnyunddit is designed with privacy as the top priority:\n\n### Data Collection\n- **No Personal Data**: Zero collection or storage of personal information\n- **No User Accounts**: No registration or login required\n- **No Cookies**: No tracking cookies or persistent storage\n- **No Analytics**: No Google Analytics, tracking pixels, or metrics collection\n\n### IP Address Handling\n- **Hashed Only**: IP addresses are SHA256 hashed for voting prevention\n- **Temporary**: Hashed IPs are deleted after 72 hours\n- **Never Stored**: Raw IP addresses are never stored in the database\n\n### Tor Network Ready\n- **Optimized**: Built specifically for .onion sites\n- **No JavaScript**: Server-side rendering eliminates JS fingerprinting\n- **Privacy Headers**: HTTP headers configured for maximum anonymity\n\n## 💬 Support\n\nNeed help or found a bug? Here's how to get support:\n\n### Bug Reports\n- Create an issue on [GitHub Issues](https://github.com/profullstack/unyunddit/issues)\n- Include steps to reproduce\n- Mention your browser and operating system\n\n### Feature Requests\n- Check [existing issues](https://github.com/profullstack/unyunddit/issues) first\n- Describe the feature and its benefits\n- Consider contributing the feature yourself!\n\n### Documentation\n- Check this README for common questions\n- Look at the [Deployment Guide](DEPLOYMENT.md) for deployment help\n- Review code comments for technical details\n\n### Security Issues\n- For security vulnerabilities, create a private issue\n- Do not publicly disclose security issues\n- We'll respond as quickly as possible\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n**Built for Privacy • Designed for Anonymity • Optimized for Tor**\n\n*Made with love by the privacy-conscious community*\n\n\u003c/div\u003e\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprofullstack%2Funyunddit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprofullstack%2Funyunddit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprofullstack%2Funyunddit/lists"}