{"id":28165495,"url":"https://github.com/outpoot/twoblade","last_synced_at":"2025-07-23T18:06:11.132Z","repository":{"id":292004665,"uuid":"970778148","full_name":"outpoot/twoblade","owner":"outpoot","description":"Interface and reference implementation of SHARP (Self-Hosted Address Routing Protocol) — a decentralized email system that uses the # symbol for addressing (e.g., user#domain.com). https://twoblade.com","archived":false,"fork":false,"pushed_at":"2025-05-29T18:21:04.000Z","size":8816,"stargazers_count":363,"open_issues_count":17,"forks_count":46,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-29T19:45:01.517Z","etag":null,"topics":["email","facedev","mail","protocol","sharp"],"latest_commit_sha":null,"homepage":"https://twoblade.com","language":"Svelte","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/outpoot.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}},"created_at":"2025-04-22T14:20:01.000Z","updated_at":"2025-05-29T18:21:07.000Z","dependencies_parsed_at":"2025-05-22T13:01:13.399Z","dependency_job_id":"c58851fb-2d44-4c04-8ec9-882650d3d47a","html_url":"https://github.com/outpoot/twoblade","commit_stats":null,"previous_names":["outpoot/twoblade"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/outpoot/twoblade","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outpoot%2Ftwoblade","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outpoot%2Ftwoblade/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outpoot%2Ftwoblade/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outpoot%2Ftwoblade/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/outpoot","download_url":"https://codeload.github.com/outpoot/twoblade/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outpoot%2Ftwoblade/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266726639,"owners_count":23974928,"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-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"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":["email","facedev","mail","protocol","sharp"],"created_at":"2025-05-15T12:11:39.596Z","updated_at":"2025-07-23T18:06:11.125Z","avatar_url":"https://github.com/outpoot.png","language":"Svelte","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg style=\"width: 128px; height: 128px\" src=\"website/static/logo.svg\" /\u003e\u003ch1 style=\"font-size: 48px\"\u003e\u003ca href=\"https://twoblade.com\"\u003eTwoblade.com\u003c/a\u003e - an email protocol \u0026 client\u003c/h1\u003e\n[Privacy Policy](https://twoblade.com/legal/privacy) | [Terms of Service](https://twoblade.com/legal/terms) | [License](LICENSE) | [YouTube video](https://youtu.be/nALc9GwZdFc)\n\n**Twoblade.com** is an interface for **SHARP** (**S**elf-**H**osted **A**ddress **R**outing **P**rotocol) - a decentralized email system that uses the `#` symbol for addressing (e.g., `user#domain.com`).\n\n## SHARP\n\n*   SHARP uses addresses in the format `user#domain.com`.\n*   `user` is the username of the recipient.\n*   `domain.com` is the domain name of the SHARP server.\n\nSHARP's HTML allows for reactive styling:\n```html\n\u003c!-- Theme-aware styling --\u003e\n\u003cdiv style=\"background: {$LIGHT ? '#ffffff' : '#1a1a1a'}\"\u003e\n\u003cp style=\"color: {$DARK ? '#ffffff' : '#000000'}\"\u003eContent\u003c/p\u003e\n\n\u003c!-- Complex conditional styling --\u003e\n\u003cdiv style=\"\n  background: {$DARK ? '#2d2d2d' : '#f0f0f0'};\n  border: {$DARK ? '1px solid #404040' : '1px solid #ddd'};\n  box-shadow: {$DARK ? '0 2px 4px rgba(0,0,0,0.5)' : '0 2px 4px rgba(0,0,0,0.1)'};\n\"\u003e\n\n\u003c!-- Available operators: $DARK, $LIGHT --\u003e\n```\n\n## Running the SHARP Server\n\n1.  **Navigate to the `SHARP` directory:**\n    ```bash\n    cd SHARP\n    ```\n\n2.  **Install dependencies:**\n    ```bash\n    bun install\n    ```\n\n3.  **Run the initialization script:**\n    ```bash\n    bash database/init.sh\n    ```\n\n4.  **Set up environment variables:**\n\n    *   The `init.sh` script will create a `.env` file in the `SHARP` directory.\n    *   It will prompt you for your domain name and set up the basic `.env` file.\n    *   You may need to modify the `.env` file to match your actual configuration, especially the `DATABASE_URL`.\n        ```\n        DATABASE_URL=postgres://user:password@host:port/database\n        SHARP_PORT=5000\n        HTTP_PORT=5001\n        DOMAIN_NAME=yourdomain.com\n        ```\n\n5.  **Run the server:**\n    ```bash\n    cd ..\n    bun run .\n    ```\n\n6.  **Add SRV records to Cloudflare (or your DNS provider):**\n\n    *   After setting up the SHARP server, you need to add SRV records to your domain's DNS settings so that other SHARP users can discover your server.\n    *   These records should point to your server's address and port.  The specific records depend on your configuration, but here's an example:\n\n        ```\n        _sharp._tcp.yourdomain.com. 86400 IN SRV 10 0 5000 yourdomain.com.\n        ```\n\n    *   Replace `yourdomain.com` with your actual domain name and `5000` with the port your SHARP server is running on (defined by `SHARP_PORT` in your `.env` file).\n    *   Consult your DNS provider's documentation for specific instructions on adding SRV records.  For Cloudflare, you can typically add these records in the DNS settings panel.\n\n## Running the Website\n\n1.  **Navigate to the `website` directory:**\n    ```bash\n    cd website\n    ```\n\n2.  **Install dependencies:**\n    ```bash\n    npm install\n    ```\n\n3.  **Set up environment variables:**\n\n    *   Create a `.env` file in the `website` directory.\n    *   Add the following variable, replacing the values with your actual configuration:\n        ```\n        PUBLIC_DOMAIN=yourdomain.com\n        ```\n\n        **Additional variables:** You may also need to configure the following variables in your `.env` file:\n        ```python\n        # Database from docker-compose\n        DATABASE_URL=postgres://postgres:REPLACE_ME@localhost:5432/twoblade\n        PUBLIC_DOMAIN=yourdomain.com\n        PUBLIC_WEBSOCKET_URL=https://localhost:3001\n\n        # The JWT secret should be long, random and similar to a password. Do not share it with anyone.\n        # Run `openssl rand -hex 64` to generate one\n        JWT_SECRET=\n\n        # S3-compatible works too.\n        PRIVATE_B2_KEY_ID=\n        PRIVATE_B2_APP_KEY=\n        PRIVATE_B2_BUCKET=\n        PRIVATE_B2_REGION=\n        PRIVATE_B2_ENDPOINT=https://s3.\u003cregion\u003e.backblazeb2.com\n\n        # A cookie from the website, optional \u0026 used in /test\n        TEST_AUTH_TOKEN=\n\n        # Comes from docker-compose\n        REDIS_URL=redis://redis:6379\n\n        # Cloudflare Turnstile keys, these are for testing \u0026 will validate any req. Replace with actual ones in prod.\n        PUBLIC_TURNSTILE_SITE_KEY=1x00000000000000000000AA\n        ```\n\n        Ensure that these URLs match the actual URLs of your API server, SHARP server, and WebSocket server.\n        \n\n4.  **Run the development server:**\n    ```bash\n    npm run dev -- --open\n    ```\n\n## Attachments Setup\nYou will need a [Backblaze](https://www.backblaze.com/) account or any S3-compatible storage provider.\n\n### Using Backblaze B2\n```bash\nwget https://github.com/Backblaze/B2_Command_Line_Tool/releases/latest/download/b2-linux -O \"b2\"\nchmod +x b2\n./b2 account authorize\n\n./b2 bucket update --cors-rules '[\n  {\n    \"corsRuleName\": \"allowS3PutFromLocalhost\",\n    \"allowedOrigins\": [\"http://localhost:5173\", \"REPLACE_ME_WITH_PUBLIC_DOMAIN\"],\n    \"allowedOperations\": [\n      \"s3_put\",\n      \"s3_get\"\n    ],\n    \"allowedHeaders\": [\"*\"],\n    \"exposeHeaders\": [\"ETag\", \"x-amz-request-id\"],\n    \"maxAgeSeconds\": 3600\n  }\n]' REPLACE_ME_WITH_BUCKET_NAME\n```\n- Note to replace `REPLACE_ME_WITH_PUBLIC_DOMAIN` and `REPLACE_ME_WITH_BUCKET_NAME`\n\n### Using other S3-Compatible storage\nYou can use any S3-compatible storage by setting these environment variables:\n```\nPRIVATE_B2_KEY_ID=\u003caccess-key\u003e\nPRIVATE_B2_APP_KEY=\u003csecret-key\u003e\nPRIVATE_B2_BUCKET=\u003cbucket-name\u003e\nPRIVATE_B2_REGION=\u003cregion\u003e\nPRIVATE_B2_ENDPOINT=\u003cs3-endpoint\u003e  # Example: https://s3.\u003cregion\u003e.amazonaws.com for AWS\n```\n\nMake sure to configure CORS rules on your bucket to allow uploads from your domain.\n\n## Running the database\n\n1.  **Change the default database password:** (optional)\n    *   Open the `docker-compose.yml` file and change `REPLACE_ME` to something else.\n        ```yaml\n        version: '3.8'\n\n        services:\n          postgres_db:\n            # ...\n            environment:\n              POSTGRES_USER: postgres\n              POSTGRES_PASSWORD: REPLACE_ME  # Replace with your desired password\n            # ...\n        ```\n    *   Update your `.env` file with the new password.\n\n2.  **Start the database:**\n    ```bash\n    docker compose up -d postgres\n# Other SHARP instances\n* ⭐ https://twoblade.com - the official client for SHARP.\n* https://garymail.org\n* https://2b.jcjenson.net/\n* https://gabserver.me/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutpoot%2Ftwoblade","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foutpoot%2Ftwoblade","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutpoot%2Ftwoblade/lists"}